P4587-[FJOI2016]神秘数【主席树】
正題
題目鏈接:https://www.luogu.com.cn/problem/P4587
題目大意
nnn個(gè)數(shù),每次選擇一個(gè)區(qū)間,然后詢問這個(gè)區(qū)間的子集和所不能表示的最小的正整數(shù)。
解題思路
假設(shè)我們從小到大加入數(shù)字,我們發(fā)現(xiàn)如果這個(gè)數(shù)不是111顯然這個(gè)區(qū)間內(nèi)至少有一個(gè)111。
我們假設(shè)現(xiàn)在能表示的最小的數(shù)是mxmxmx,加入一個(gè)數(shù)xxx時(shí),如果x>mx+1x> mx+1x>mx+1那么mx+1mx+1mx+1就不能表現(xiàn)出來,否則我們就可以表示出1~mx+x1\sim mx+x1~mx+x的數(shù),即讓mx=mx+xmx=mx+xmx=mx+x。
考慮如何優(yōu)化,如果我們之前能表現(xiàn)出1~mx1\sim mx1~mx,加上某些數(shù)之后能表現(xiàn)出1~mx′1\sim mx'1~mx′了,那么我們顯然可以尋找任何一個(gè)在mx+1~mx′+1mx+1\sim mx'+1mx+1~mx′+1里的數(shù)字來擴(kuò)充,那么我們之間將這段區(qū)間的和取來不就好了嗎。這個(gè)可以用主席樹維護(hù)。
時(shí)間復(fù)雜度如何,我們發(fā)現(xiàn)每次找到的區(qū)間和如果還能往下顯然是大于mxmxmx的,然后會讓mx′+mxmx'+mxmx′+mx也就是每次至少可以讓mx′mx'mx′加上自己的一半,這個(gè)時(shí)間復(fù)雜度是接近倍增也就是logloglog的。
所以時(shí)間復(fù)雜度O(nlog?2n)O(n\log^2 n)O(nlog2n)
codecodecode
#include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; const ll N=1e6+10,M=5e6+10,inf=1e9; ll n,m,cnt,rt[N]; ll ls[M],rs[M],sum[M]; ll Change(ll x,ll L,ll R,ll val){ll y=++cnt;rs[y]=rs[x];ls[y]=ls[x];ll mid=(L+R)>>1;sum[y]=sum[x]+val;if(L==R)return y;if(val<=mid)ls[y]=Change(ls[x],L,mid,val);else if(val>mid)rs[y]=Change(rs[x],mid+1,R,val);return y; } ll Ask(ll x,ll y,ll L,ll R,ll l,ll r){if(!(sum[x]-sum[y]))return 0;if(l==L&&r==R)return sum[x]-sum[y];ll mid=(L+R)>>1;if(r<=mid)return Ask(ls[x],ls[y],L,mid,l,r);if(l>mid)return Ask(rs[x],rs[y],mid+1,R,l,r);return Ask(ls[x],ls[y],L,mid,l,mid)+Ask(rs[x],rs[y],mid+1,R,mid+1,r); } int main() {scanf("%lld",&n);for(ll i=1;i<=n;i++){ll x;scanf("%lld",&x);rt[i]=Change(rt[i-1],1,inf,x);}scanf("%lld",&m);for(ll i=1;i<=m;i++){ll l,r,lim=0,at=0;scanf("%lld%lld",&l,&r);while(1){ll w=Ask(rt[r],rt[l-1],1,inf,lim+1,at+1);if(!w)break;lim=at+1;at+=w;}printf("%lld\n",at+1);}return 0; }總結(jié)
以上是生活随笔為你收集整理的P4587-[FJOI2016]神秘数【主席树】的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 脚气怎么根治,治脚气最有效的方法
- 下一篇: P3899-[湖南集训]谈笑风生【主席树