题链:
http://www.lydsy.com/JudgeOnline/problem.php?id=4408
题解:
主席树
首先,对于一些数来说,
如果可以我们可以使得其中的某些数能够拼出 1~ret
那么此时的ANS(神秘数)= ret+1
然后考虑,如果此时存在另一个数小于等于 ANS,(设该数为 x)
则一定可以在原来的1~ret的基础上拼出 1~ret+x
即 ANS 可以更新为 ret+x+1
所以具体的操作就是:
每次查询区间内小于ANS的数的和(SUM),然后如果SUM大于ANS,则更新ANS为SUM+1。
不断上述操作直到SUM<ANS为止。
主席数实现在序列区间中查询权值区间的和。
代码:
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define MAXN 100500 using namespace std; int A[MAXN],tmp[MAXN]; int N,M,tnt; struct CMT{ long long sum[MAXN*20]; int rt[MAXN],ls[MAXN*20],rs[MAXN*20],sz; void Insert(int v,int &u,int l,int r,int p){ u=++sz; ls[u]=ls[v]; rs[u]=rs[v]; sum[u]=sum[v]; sum[u]+=tmp[p]; if(l==r) return; int mid=(l+r)>>1; if(p<=mid) Insert(ls[v],ls[u],l,mid,p); else Insert(rs[v],rs[u],mid+1,r,p); } long long Query(int v,int u,int l,int r,int al,int ar){ if(al<=l&&r<=ar) return sum[u]-sum[v]; int mid=(l+r)>>1; long long ret=0; if(al<=mid) ret+=Query(ls[v],ls[u],l,mid,al,ar); if(mid<ar) ret+=Query(rs[v],rs[u],mid+1,r,al,ar); return ret; } void Build(){ for(int i=1;i<=N;i++) Insert(rt[i-1],rt[i],1,tnt,A[i]); } }DT; int main(){ // freopen("/home/noilinux/Documents/Code/BZOJ/4408.in","r",stdin); // printf("BEGIN.\n"); scanf("%d",&N); for(int i=1;i<=N;i++) scanf("%d",&A[i]),tmp[i]=A[i]; sort(tmp+1,tmp+N+1); tnt=unique(tmp+1,tmp+N+1)-tmp-1; for(int i=1;i<=N;i++) A[i]=lower_bound(tmp+1,tmp+tnt+1,A[i])-tmp; scanf("%d",&M); DT.Build(); long long ANS,ret,p; for(int i=1,l,r;ANS=0,ret=0,i<=M;i++){ scanf("%d%d",&l,&r); while(ANS<ret+1){ ANS=ret+1; p=upper_bound(tmp+1,tmp+tnt+1,ANS)-tmp-1; ret=DT.Query(DT.rt[l-1],DT.rt[r],1,tnt,1,p); } printf("%lld\n",ANS); } return 0; }
时间: 2024-11-07 16:28:02