BZOJ4299 Codechef FRBSUM(主席树)

  感觉非常不可做,于是考虑有什么奇怪的性质。

  先考虑怎么求子集和mex。将数从小到大排序,假设已经凑出了0~n的所有数,如果下一个数>n+1显然mex就是n+1了,否则若其为x则可以凑出1~n+x所有数。

  对于区间查询,建棵主席树即可,每次查询权值线段树上lastn+2~n+1的区间,用区间和更新n,如果这段区间没有数则mex为n+1。因为每次n的增量都是在lastn+2~n+1这一段的,所以每查询两次n会翻一倍以上,复杂度O(nlog2n)。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
int read()
{
    int x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
#define N 100010
#define inf 1000000000
int n,m,a[N],root[N],cnt;
struct data{int l,r,x;
}tree[N<<6];
void ins(int &k,int l,int r,int x)
{
    tree[++cnt]=tree[k],k=cnt;tree[k].x+=x;
    if (l==r) return;
    int mid=l+r>>1;
    if (x<=mid) ins(tree[k].l,l,mid,x);
    else ins(tree[k].r,mid+1,r,x);
}
int query(int x,int y,int l,int r,int p,int q)
{
    if (!y) return 0;
    if (p==l&&q==r) return tree[y].x-tree[x].x;
    int mid=l+r>>1;
    if (q<=mid) return query(tree[x].l,tree[y].l,l,mid,p,q);
    else if (p>mid) return query(tree[x].r,tree[y].r,mid+1,r,p,q);
    else return query(tree[x].l,tree[y].l,l,mid,p,mid)+query(tree[x].r,tree[y].r,mid+1,r,mid+1,q);
}
int getans(int l,int r)
{
    int sum=0,last=-1;
    while (sum<inf)
    {
        int x=query(root[l],root[r],1,inf,last+2,sum+1);
        if (!x) return sum+1;
        else last=sum,sum+=x;
    }
    return sum;
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("bzoj4299.in","r",stdin);
    freopen("bzoj4299.out","w",stdout);
    const char LL[]="%I64d
";
#else
    const char LL[]="%lld
";
#endif
    n=read();
    for (int i=1;i<=n;i++) a[i]=read();
    for (int i=1;i<=n;i++)
    {
        root[i]=root[i-1];
        ins(root[i],1,inf,a[i]);
    }
    m=read();
    for (int i=1;i<=m;i++)
    {
        int x=read(),y=read();
        printf("%d
",getans(x-1,y));
    }
    return 0;
}
原文地址:https://www.cnblogs.com/Gloid/p/9839785.html