BZOJ 4631: 踩气球
对于每个点开一棵权值线段树,以区间的左端点为值插入右端点的权值线段树中,并查集维护i左边第一个不被删空的位置,为F[i]
当一个点被删空的时候,这棵线段树中>F[i]的位置即为答案,清空以后把i和F[i]这两棵线段树合并
#include<cstdio> using namespace std; int cnt,Lastans,F[1000005],a[1000005],ls[2000005],rs[2000005],sz[2000005],root[100005]; int find(int x){ if (!a[F[x]]) F[x]=find(F[x]); return F[x]; } void update(int t){ sz[t]=sz[ls[t]]+sz[rs[t]]; } void del(int t,int l,int r,int x,int y){ if (!t) return; if (r<x || l>y) return; if (l>=x && r<=y){ Lastans+=sz[t]; sz[t]=0; return; } int mid=(l+r)>>1; del(ls[t],l,mid,x,y); del(rs[t],mid+1,r,x,y); update(t); } void insert(int &t,int l,int r,int x){ if (!t) t=++cnt; sz[t]++; if (l==r) return; int mid=(l+r)>>1; if (x<=mid) insert(ls[t],l,mid,x); else insert(rs[t],mid+1,r,x); } int merge(int x,int y){ if (!x || !y) return x^y; sz[x]+=sz[y]; ls[x]=merge(ls[x],ls[y]); rs[x]=merge(rs[x],rs[y]); return x; } int main(){ int n,m; scanf("%d%d",&n,&m); for (int i=1; i<=n; i++) scanf("%d",&a[i]); for (int i=1; i<=n; i++) F[i]=i-1; for (int i=1; i<=m; i++){ int l,r; scanf("%d%d",&l,&r); insert(root[r],1,n,l); } a[0]=1; int q; scanf("%d",&q); while (q--){ int x; scanf("%d",&x); x=(x+Lastans-1)%n+1; a[x]--; if (!a[x]){ int fx=find(x); del(root[x],1,n,fx+1,n); root[fx]=merge(root[fx],root[x]); } printf("%d\n",Lastans); } return 0; }