bzoj4631 踩气球
题意:给定一个长为n的正整数序列,并给定m个区间,q次操作,每次操作将一个位置的数值减1,并在操作后输出给定的m个区间中有多少个区间的区间和为0.强制在线.
数据范围:n,m,q<=10^5
首先只有某个位置x的气球数目从1变成0的时候才会对答案产生影响,那么我们考虑这时什么样的区间的区间和会变成0.这样的区间之前一定包含位置x而且只有位置x不为0,也就是由位置x再加上x左边一段0和x右边一段0组成(这两段0可能不存在).我们找出x左边尽量长的一段连续的0和x右边尽量长的一段连续的0(这两段连续的0的长度可以用链表或者并查集维护,链表维护的方法见claris题解,我写的是无脑并查集).那么符合条件的区间的左端点在一个范围内,右端点在一个范围内,这种二维的查询就可以用主席树来做,按照左端点插入区间,将右端点对应的权值线段树可持久化就可以了.只有某个位置的数值变为0的时候才会在主席树上查询,复杂度为O(mlogn)建树+O(nlogn)查询.并查集还有个比较小的O(nlogn).
#include<cstdio> #include<vector> #include<algorithm> using namespace std; const int maxn=100005; struct node{ node* ch[2]; int sum; node(){} node(int x){sum=x;ch[0]=ch[1]=0;} }t[maxn*50];int cnt=0; node* newnode(int x){ t[++cnt]=node(x);return t+cnt; } node* root[maxn]; int ufs[maxn],l[maxn],r[maxn]; int find(int x){ return ufs[x]==x?x:ufs[x]=find(ufs[x]); } void link(int a,int b){ if(find(a)==find(b))return; int ra=find(a),rb=find(b); ufs[ra]=rb;l[rb]=min(l[rb],l[ra]);r[rb]=max(r[rb],l[rb]); } int a[maxn]; vector<int> right[maxn]; void Insert(node* rt0,node* &rt,int l,int r,int x){ rt=newnode(rt0->sum+1); if(l==r)return; int mid=(l+r)>>1; if(x<=mid){ Insert(rt0->ch[0],rt->ch[0],l,mid, x); rt->ch[1]=rt0->ch[1]; }else{ Insert(rt0->ch[1],rt->ch[1],mid+1,r,x); rt->ch[0]=rt0->ch[0]; } } int query(node* rt0,node* rt1,int l,int r,const int &ql,const int &qr){ if(ql<=l&&r<=qr)return rt1->sum-rt0->sum; int mid=(l+r)>>1,ans=0; if(ql<=mid)ans+=query(rt0->ch[0],rt1->ch[0],l,mid,ql,qr); if(qr>mid) ans+=query(rt0->ch[1],rt1->ch[1],mid+1,r,ql,qr); return ans; } int main(){ int n,m;scanf("%d%d",&n,&m); for(int i=1;i<=n;++i){ ufs[i]=l[i]=r[i]=i; } for(int i=1;i<=n;++i)scanf("%d",&a[i]); int x,y; for(int i=1;i<=m;++i){ scanf("%d%d",&x,&y); right[x].push_back(y); } root[0]=t;root[0]->ch[0]=root[0]->ch[1]=t;root[0]->sum=0; for(int i=1;i<=n;++i){ root[i]=root[i-1]; for(vector<int>::iterator pt=right[i].begin();pt!=right[i].end();++pt)Insert(root[i],root[i],1,n,*pt); }a[0]=a[n+1]=0x7f7f7f7f; int ans=0; int q;scanf("%d",&q); for(int i=1;i<=q;++i){ scanf("%d",&x);x=(x+ans-1+n)%n+1; a[x]--; if(a[x]==0){ int lo=x,hi=x; if(a[x-1]==0)lo=l[find(x-1)]; if(a[x+1]==0)hi=r[find(x+1)];//printf("%d %d %d %d\n",lo,x,x,hi); ans+=query(root[lo-1],root[x],1,n,x,hi); if(a[x-1]==0)link(x-1,x); if(a[x+1]==0)link(x,x+1); } printf("%d\n",ans); } return 0; }