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;
}

 

posted @ 2017-03-12 10:51  liu_runda  阅读(565)  评论(0编辑  收藏  举报
偶然想到可以用这样的字体藏一点想说的话,可是并没有什么想说的. 现在有了:文化课好难