4408: [Fj Winter Camp 2016]神秘数&&4299: Codechef FRBSUM|主席树

好神的一道题!
很容易发现假如已经构造出来了 1,2...max 再插入一个数 x ,假如 xmax+1 那么现在就可以构造出 1,2...max,max+1...max+x
于是我们可以这样做,先建立主席树。对于询问区间 [l,r] ,当前的 max=0 ,然后不断查询区间 [l,r] 1 max+1 所有数的和来更新 max
如果区间 [l,r] 1 max+1 所有数的和 >max ,那么肯定可以构造出 1 到这个所有数的和之间的数,然后一直更新,直到不能更新为止。
可以发现更新这个过程 max 是成倍增长的,然后每次询问的复杂度都是 log 的,所以总的复杂度就是 mlog2n

#include<bits/stdc++.h>
#define N 100005
using namespace std;
int sc()
{
    int i=0; char c=getchar();
    while(c>'9'||c<'0')c=getchar(); 
    while(c>='0'&&c<='9')i=i*10+c-'0',c=getchar();
    return i;
}
int ch[N*40][2],sum[N*40],a[N],root[N];
int n,m,mx,cnt;
void add(int pre,int &x,int l,int r,int v)
{
    if(!x)x=++cnt;
    sum[x]=sum[pre]+v;
    if(l==r)return;
    int mid=l+r>>1;
    if(v<=mid)
        ch[x][1]=ch[pre][1],
        add(ch[pre][0],ch[x][0],l,mid,v);
    else
        ch[x][0]=ch[pre][0],
        add(ch[pre][1],ch[x][1],mid+1,r,v);
}
int query(int pre,int x,int l,int r,int E)
{
    if(E>=r)return sum[x]-sum[pre];
    int mid=l+r>>1;
    if(E>mid)
        return sum[ch[x][0]]-sum[ch[pre][0]]+query(ch[pre][1],ch[x][1],mid+1,r,E);
    else  return query(ch[pre][0],ch[x][0],l,mid,E);
}   
int main()
{
    n=sc();
    for(int i=1;i<=n;i++)
        mx=max(mx,a[i]=sc());
    for(int i=1;i<=n;i++)
        add(root[i-1],root[i],1,mx,a[i]);
    m=sc();
    for(int i=1;i<=m;i++)
    {
        int l=sc()-1,r=sc(),ans=1;
        while(1)
        {
            int sum=query(root[l],root[r],1,mx,ans);
            if(sum<ans)break;
            ans=sum+1;
        }
        printf("%d\n",ans);
    }
    return 0;
}

你可能感兴趣的:(主席树)