luogu P4587 [FJOI2016]神秘数
我们首先考虑暴力如何做。
现在最大的问题就是如何找出这个神秘数
先按升序排序,然后一个一个加数。
假设现在的值域是[1,pos],当前要加入的数是 x ;
不难发现,有两种情况:
若x>pos+1 , 那么一定拼不出pos+1,答案即为pos+1;
若1<= x <= pos+1 , 那么现在的值域就变成了[1,pos+x];
这是最暴力的方法。。
优化:
考虑一段区间能够做出的贡献。
假设当前的值域为[1,pos],mx为上次用来更新值域时统计到的最大的范围 , 那么现在能做出贡献的一定有[mx+1,pos+1],统计出这段区间的sum。
(mx及之前的就不用统计了啦,因为在上一次更新值域时已经加过了)
现在的值域就变成了[1,pos+sum] , mx就变成了pos+1 ;
若sum==0,则说明[mx+1,pos+1]这段区间里没有数,那么答案就是pos+1啦(因为后面的数一定大于pos+1)。。
额,至于要快速找某段区间的某个范围的和,当然是主席树,然后就木有了。
Code
#include<iostream> #include<cstdio> #define N 100010 using namespace std; int n,m,a[N],tot,root[N]; const int INF=1e9; struct Node{ int lc,rc,sum; }t[5000000]; inline int read(){ char c=getchar();int x=0,flag=1; while(c<'0' || c>'9') {if(c=='-') flag=-1;c=getchar();} while(c>='0' && c<='9') x=(x<<1)+(x<<3)+c-'0',c=getchar(); return x*flag; } int insert(int q,int l,int r,int x,int val){ int p=++tot;t[p]=t[q];if(l==r){t[p].sum+=val;return p;} int mid=(l+r)>>1; if(x<=mid) t[p].lc=insert(t[q].lc,l,mid,x,val); else t[p].rc=insert(t[q].rc,mid+1,r,x,val); t[p].sum=t[t[p].lc].sum+t[t[p].rc].sum; return p; } int Query(int p,int q,int l,int r,int ls,int rs){ if(ls<=l && rs>=r) return t[p].sum-t[q].sum; int mid=(l+r)>>1,val=0; if(ls<=mid) val+=Query(t[p].lc,t[q].lc,l,mid,ls,rs); if(rs>mid) val+=Query(t[p].rc,t[q].rc,mid+1,r,ls,rs); return val; } int main(){ n=read(); for(int i=1;i<=n;i++){a[i]=read();root[i]=insert(root[i-1],1,INF,a[i],a[i]);} m=read(); while(m--){ int l=read(),r=read(),mx=0,pos=0,sum=0; while(true){ sum=Query(root[r],root[l-1],1,INF,mx+1,pos+1); if(!sum){printf("%d\n",pos+1);break;} mx=pos+1;pos+=sum; } } return 0; }