整体二分的复习

上次写接水果的时候看出来是整体二分了 但是板子有点忘了所以复习一下。
上例题:LINK:bzoj2527

好像以前写过 那就再写一遍.

求每个国家收集齐陨石的最早时间。

模拟显然过不了 我们可以二分一个时间。

考虑判定 如果我们能求出sum[i][j]表示前i个时间之内对于j国家能收集到的陨石个数那么即可完成判定。

显然在求出这个数组的时候我们都能求出来答案了...

所有的询问都要二分,所以直接上整体二分。二分内部线段树区间修改+每个国家暴力查询即可。

复杂度 mlog^2. 区间修改单点查询 直接上树状数组即可. 这题卡常 我想用线段树是不可能过掉这道题的... ``` const int MAXN=300010; int n,m,p,t; int a[MAXN],ans[MAXN]; LL c[MAXN]; struct wy { int op,x,y; int k; }q[MAXN<<1],ql[MAXN<<1],qr[MAXN<<1]; inline void add(RE int x,RE int y) { while(x<=m) { c[x]+=y; x+=x&(-x); } return; } inline LL ask(RE int x) { LL cnt=0; while(x) { cnt+=c[x]; x-=x&(-x); } return cnt; } vectorw[MAXN]; inline void add1(int x,int y,int z) { add(x,z); add(y+1,-z); } inline void solve(int l,int r,int L,int R) { if(l>r)return; if(L==R) { for(int i=l;i<=r;++i)if(op(i)==0)ans[x(i)]=L; return; } RE int mid=(L+R)>>1; RE int ll=0,rr=0; for(int i=l;i<=r;++i) { if(!op(i)) { RE int x=x(i);RE LL cnt=0; for(int j=0;j=k(i))break; } if(cnt>=k(i))ql[++ll]=q[i]; else k(i)-=cnt,qr[++rr]=q[i]; } else { if(op(i)<=mid) { ql[++ll]=q[i]; if(x(i)>y(i))add1(x(i),m,k(i)),add1(1,y(i),k(i)); else add1(x(i),y(i),k(i)); } else qr[++rr]=q[i]; } } for(int i=1;i<=ll;++i) { if(ql[i].op) { if(ql[i].x>ql[i].y)add1(ql[i].x,m,-ql[i].k),add1(1,ql[i].y,-ql[i].k); else add1(ql[i].x,ql[i].y,-ql[i].k); } q[l+i-1]=ql[i]; } for(int i=1;i<=rr;++i)q[l+ll+i-1]=qr[i]; solve(l,l+ll-1,L,mid); solve(l+ll,r,mid+1,R); } int main() { freopen("1.in","r",stdin); n=read();m=read(); for(RE int i=1;i<=m;++i) { RE int x; x=read(); w[x].push_back(i); } for(RE int i=1;i<=n;++i)a[i]=read(); p=read(); for(RE int i=1;i<=p;++i) { op(++t)=i;x(t)=read(); y(t)=read();k(t)=read(); } for(RE int i=1;i<=n;++i)op(++t)=0,x(t)=i,k(t)=a[i]; solve(1,t,1,p+1); for(int i=1;i<=n;++i)if(ans[i]!=p+1)printf("%d\n",ans[i]);else puts("NIE"); return 0; } ```

posted @ 2020-03-09 19:40  chdy  阅读(96)  评论(0编辑  收藏  举报