题意:一共有n个盒子,每个盒子中有ai个气球。共进行q轮操作,在x盒子中踩烂一个气球,并询问有多少的小朋友是高兴的?
高兴:一共有m个小孩子,每个小孩子的区间为[li,ri],当这个区间盒子中的所有气球都被踩烂他就高兴。
n<=1e5。
标程:
1 #include<cstdio> 2 #define mid ((l+r)>>1) 3 using namespace std; 4 typedef long long ll; 5 int read() 6 { 7 int x=0;char ch=getchar(); 8 while (ch<'0'||ch>'9') ch=getchar(); 9 while ('0'<=ch&&ch<='9') x=x*10+ch-'0',ch=getchar(); 10 return x; 11 } 12 const int N=100005; 13 int seg[N],head[N<<2],cnt,n,m,q,l,r,x,ans;//注意head是线段树上的点连边,大小开4倍! 14 ll sum[N<<2];//注意气球总数很多,要开ll! 15 struct node{int to,next;}num[N*30]; 16 void add(int x,int y) 17 {num[++cnt].to=y;num[cnt].next=head[x];head[x]=cnt;} 18 void build(int k,int l,int r) 19 { 20 if (l==r) {sum[k]=read();return;} 21 build(k<<1,l,mid);build(k<<1|1,mid+1,r); 22 sum[k]=sum[k<<1]+sum[k<<1|1]; 23 } 24 void ins(int k,int l,int r,int L,int R,int x) 25 { 26 if (L<=l&&r<=R) {add(k,x);seg[x]++;return;} 27 if (L<=mid) ins(k<<1,l,mid,L,R,x); 28 if (R>mid) ins(k<<1|1,mid+1,r,L,R,x); 29 } 30 void work(int x) 31 { 32 for (int i=head[x];i;i=num[i].next) 33 if (!--seg[num[i].to]) ans++; 34 } 35 void modi(int k,int l,int r,int x) 36 { 37 sum[k]--;if (!sum[k]) work(k); 38 if (l==r) return; 39 if (x<=mid) modi(k<<1,l,mid,x); 40 else modi(k<<1|1,mid+1,r,x); 41 } 42 int main() 43 { 44 n=read();m=read();build(1,1,n); 45 for (int i=1;i<=m;i++) l=read(),r=read(),ins(1,1,n,l,r,i); 46 q=read(); 47 while (q--) 48 { 49 x=(read()+ans-1)%n+1; 50 modi(1,1,n,x); printf("%d\n",ans); 51 } 52 return 0; 53 }
题解:线段树优化建边
线段树维护的是气球盒子的区间。
每个小孩有一段区间,我们将这一段区间拆分成logn个对相应线段树上的区间连边。当这logn个区间的sum都为0时,他高兴。
对于一个踩气球的操作,相当于是单点修改-1。如果一段区间的sum=0,那么就把依赖它的小孩的seg(仍存在的依赖区间数)-1,如果某小孩的seg=0,那么ans++。
时间复杂度O((n+q)logn)。