2527: [Poi2011]Meteors
Time Limit: 60 Sec Memory Limit: 128 MBSubmit: 1528 Solved: 556
[Submit][Status][Discuss]
Description
这个星球经常会下陨石雨。BIU已经预测了接下来K场陨石雨的情况。 BIU的第i个成员国希望能够收集Pi单位的陨石样本。你的任务是判断对于每个国家,它需要在第几次陨石雨之后,才能收集足够的陨石。 输入: 第一行是两个数N,M。 第二行有M个数,第i个数Oi表示第i段轨道上有第Oi个国家的太空站。 第三行有N个数,第i个数Pi表示第i个国家希望收集的陨石数量。 第四行有一个数K,表示BIU预测了接下来的K场陨石雨。 接下来K行,每行有三个数Li,Ri,Ai,表示第K场陨石雨的发生地点在从Li顺时针到Ri的区间中(如果Li<=Ri,就是Li,Li+1,...,Ri,否则就是Ri,Ri+1,...,m-1,m,1,...,Li),向区间中的每个太空站提供Ai单位的陨石样本。 输出: N行。第i行的数Wi表示第i个国家在第Wi波陨石雨之后能够收集到足够的陨石样本。如果到第K波结束后仍然收集不到,输出NIE。 数据范围:
Input
Output
Sample Input
1 3 2 1 3
10 5 7
3
4 2 4
1 3 1
3 5 2
Sample Output
NIE
1
HINT
Source
[分析]:
对于单个查询(假设为第i个国家),我们可以二分k,每次对于一个区间[l,r],手动模拟一下在第mid场流星雨过后,第i个国家一共收集到了多少单位的陨石,如果比pi大,那么答案在[l,mid]范围内,否则答案在[mid+1,r]范围内。
对于多组查询,我们也可以这么做——整体二分。首先,我们需要用一个列表id[]记录所有查询的编号,刚开始的时候,id[]自然是递增的.同时,我们用一个数组lans[i]记录下,第i个国家在l-1场流星雨过后,收集到的陨石的数目。
主过程为void divide(int opl,int opr,int l,int r),表示对于id[opl]到id[opr]的所有询问,在[l,r]范围内查询答案,通过上一层的操作,我们保证id[opl]到id[opr]的所有询问的答案都在[l,r]范围内。
首先,我们先模拟[l,mid]这么多次操作(在询问重新划分之后,必须要再次模拟,将数组清空),用树状数组(推荐“常数小”)或者是线段树计算出在[l,mid]场流星雨之后,每个空间站收集到的陨石的数目。
然后我们查询,每个国家收集到的陨石的数目,要注意的是,我们需要用链表储存每个国家对应的空间站,并且一一枚举,用nans[id[i]]表示国家id[i]收集到的陨石的数目。
那么从[1,mid]这么多次操作之后,国家id[i]收集到的陨石数目就是nans[id[i]]+lans[id[i]],如果nans[id[i]]+lans[id[i]]>P[id[i]],那么表明对于国家id[i],其答案在[l,mid]这个范围内,否则其答案在[mid+1,r]范围内,并将nans[id[i]]累加到lans[id[i]]上。
还有一个坑点是,nans[id[i]]可能很大,会爆掉long long,所以如果枚举一个国家的所有空间站的时候,发现nans[id[i]]已经大于P[id[i]]了,那么就break好了,不然会出错。
因为可能会出现怎么也无法满足的情况,所以我们需要多增加一场流星雨,这场流星雨的数量为+oo,保证能够让所有国家都满足要求,那么最后,对于所有答案为k+1的询问,输出NIE就行了。
#include<cstdio> #include<cstring> #include<iostream> #define lowbit(x) (x&-x) using namespace std; typedef long long ll; inline void read(int &x){ register char ch=getchar();x=0; while(ch<'0'||ch>'9') ch=getchar(); while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar(); } const int N=3e5+5; const ll oo=0x7ffffffffffffLL; struct query{ int x,y;ll d; query(){} query(int _x,int _y,ll _d){ x=_x,y=_y,d=_d; } }q[N];int m,n,k,P[N],ans[N]; int id[N],idl[N],idr[N]; ll nans[N],lans[N]; int tot,to[N],head[N],next[N]; struct BIT{ ll c[N]; inline void clr(){memset(c,0,sizeof c);} inline void plus(int p,ll v){ for(int i=p;i<=m;i+=lowbit(i)) c[i]+=v; } inline void opera(int l,int r,ll v){ plus(l,v);plus(r+1,-v); } inline ll qsum(int &p){ ll res=0; for(int i=p;i;i-=lowbit(i)) res+=c[i]; return res; } }bit; inline void add(int x,int y){ to[++tot]=y;next[tot]=head[x];head[x]=tot; } //链表记录国家对应环上的点 //nans记入当前区间的贡献(贡献:当前区间所有不同点的陨石量) //lans记入上一区间(l-1区间)的贡献 void divide(int opl,int opr,int l,int r){ if(opl>opr||l>r) return ; if(l==r){ for(int i=opl;i<=opr;i++) ans[id[i]]=l; return ; } int ql=0,qr=0; int mid=(l+r)>>1; for(int i=l;i<=mid;i++){ if(q[i].x<=q[i].y) bit.opera(q[i].x,q[i].y,q[i].d); else bit.opera(q[i].x,m,q[i].d),bit.opera(1,q[i].y,q[i].d); } for(int i=opl;i<=opr;i++){ nans[id[i]]=0; for(int j=head[id[i]];j;j=next[j]){ nans[id[i]]+=bit.qsum(to[j]); if(nans[id[i]]+lans[id[i]]>=(ll)P[id[i]]) break; } if(nans[id[i]]+lans[id[i]]>=(ll)P[id[i]]) idl[++ql]=id[i]; else idr[++qr]=id[i],lans[id[i]]+=nans[id[i]]; } for(int i=l;i<=mid;i++){ if(q[i].x<=q[i].y) bit.opera(q[i].x,q[i].y,-q[i].d); else bit.opera(q[i].x,m,-q[i].d),bit.opera(1,q[i].y,-q[i].d); } for(int i=1;i<=ql;i++) id[opl+i-1]=idl[i]; for(int i=1;i<=qr;i++) id[opl+ql+i-1]=idr[i]; divide(opl,opl+ql-1,l,mid); divide(opl+ql,opr,mid+1,r); } int main(){ read(n);read(m); for(int i=1,x;i<=m;i++) read(x),add(x,i); for(int i=1,x;i<=n;i++) read(P[i]),id[i]=i; read(k); for(int i=1,l,r,x;i<=k;i++) read(l),read(r),read(x),q[i]=query(l,r,x); q[++k]=query(1,m,oo); divide(1,n,1,k); for(int i=1;i<=n;i++) if(ans[i]!=k) printf("%d ",ans[i]);else puts("NIE"); return 0; }