欢迎访问~原文出处——博客园-zhouzhendong
去博客园看该题解
题目传送门 - BZOJ3110
题意概括
有N个位置,M个操作。操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c。如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少。
N,M<=50000
a<=b<=N
1操作中abs(c)<=N
2操作中c<=Maxlongint
UPD(2018-04-01):之前抄的树套树是真的丑到爆。今天用分治做了一遍QAQ。
题解
让我们来考虑神奇的分治算法。
整体二分!!(当你会了)
首先当你已经掌握了树状数组的区间加和区间询问(如果不会->点这里)
我们考虑二分答案。
注意进行以下操作要严格按照输入时间先后顺序来。
首先对于加进去的数字c,我们把他变成n-c+1,这样就把询问前k大变成了前k小。
如果是修改操作,如果修改的值比当前的mid值小,就修改,并扔到左区间里面。否则扔到右边。
如果是询问操作,如果在当前的状态下,该询问的区间内查询到的数的个数res比当前询问的c要大(或者相等),那么显然答案在左区间,把他扔到左边,否则把他的c减掉res再扔到右边去。
然后递归分治两个区间就可以了。
(本质是个二分答案的升级版)
然而博主非常非常非常非常非常非常的菜。千辛万苦调出样例,交一发WA。找了半天发现树状数组打萎掉了。
然后推式子不下于3遍。校对lych大佬的代码不下于5遍,还是没发现错误。
woc心态爆炸bonebonebone!
还是没发现错误。
发现了。最难发现的地方。tree[2][N]打成了tree[N][2]……QAQ
代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; const int N=50005; int n,m,id[N],tmpL[N],tmpR[N]; LL tree[2][N]; int lowbit(int x){ return x&-x; } void add(int t,int x,int y){ for (;x<=n+1;x+=lowbit(x)) tree[t][x]+=y; } void update(int L,int R,int v){ add(0,L,v),add(1,L,v*L); add(0,R+1,-v),add(1,R+1,-v*(R+1)); } LL sum(int t,int x){ LL ans=0; for (;x>0;x-=lowbit(x)) ans+=tree[t][x]; return ans; } LL query(int L,int R){ return sum(0,R)*(R+1)-sum(0,L)*L-sum(1,R)+sum(1,L); } struct opts{ int type,a,b,c,ans; void get(){ scanf("%d%d%d%d",&type,&a,&b,&c); if (type==1) c=n-c+1; } }a[N]; void solve(int xL,int xR,int L,int R){ if (L>R) return; if (xL==xR){ for (int i=L;i<=R;i++) a[id[i]].ans=xL; return; } int xmid=(xL+xR)>>1; int l=0,r=0; for (int i=L;i<=R;i++) if (a[id[i]].type==1){ if (a[id[i]].c<=xmid) tmpL[++l]=id[i],update(a[id[i]].a,a[id[i]].b,1); else tmpR[++r]=id[i]; } else { LL res=query(a[id[i]].a,a[id[i]].b); if (res>=a[id[i]].c) tmpL[++l]=id[i]; else tmpR[++r]=id[i],a[id[i]].c-=res; } for (int i=1;i<=l;i++) if (a[tmpL[i]].type==1) update(a[tmpL[i]].a,a[tmpL[i]].b,-1); for (int i=L;i<=L+l-1;i++) id[i]=tmpL[i-(L-1)]; for (int i=R-r+1;i<=R;i++) id[i]=tmpR[i-(R-r)]; solve(xL,xmid,L,L+l-1); solve(xmid+1,xR,R-r+1,R); } int main(){ scanf("%d%d",&n,&m); for (int i=1;i<=m;i++) a[i].get(),id[i]=i; memset(tree,0,sizeof tree); solve(1,2*n+1,1,m); for (int i=1;i<=m;i++) if (a[i].type==2) printf("%d ",n-a[i].ans+1); return 0; }
———————old———————(2017-12-19)
题解
树套树裸题。
外层套权值线段树,内层套区间线段树。
标记永久化比较好写。
空间随便卡卡就过去了。
代码
#include <cstring> #include <algorithm> #include <cstdio> #include <cstdlib> #include <cmath> using namespace std; typedef long long LL; const int N=50005,NN=N*2,K=220; struct Position_Segment_Tree{ int tot,ls[N*K],rs[N*K]; LL add[N*K],sum[N*K]; void clear(){ tot=0; memset(ls,0,sizeof ls); memset(rs,0,sizeof rs); memset(add,0,sizeof add); memset(sum,0,sizeof sum); } void update(int &rt,int le,int ri,int xle,int xri){ if (!rt) rt=++tot; if (xle<=le&&ri<=xri){ add[rt]++; return; } sum[rt]+=xri-xle+1; int mid=(le+ri)>>1; if (xri<=mid) update(ls[rt],le,mid,xle,xri); else if (xle>mid) update(rs[rt],mid+1,ri,xle,xri); else { update(ls[rt],le,mid,xle,mid); update(rs[rt],mid+1,ri,mid+1,xri); } } LL query(int rt,int le,int ri,int xle,int xri){ if (!rt) return 0; if (xle<=le&&ri<=xri) return sum[rt]+add[rt]*(xri-xle+1); int mid=(le+ri)>>1; LL res=add[rt]*(xri-xle+1); if (xri<=mid) return res+query(ls[rt],le,mid,xle,xri); else if (xle>mid) return res+query(rs[rt],mid+1,ri,xle,xri); else return res+query(ls[rt],le,mid,xle,mid) +query(rs[rt],mid+1,ri,mid+1,xri); } }PST; int n,nn,m; int tr[NN*4]; void update(int rt,int le,int ri,int pos,int xle,int xri){ PST.update(tr[rt],1,n,xle,xri); if (le==ri) return; int mid=(le+ri)>>1,ls=rt<<1,rs=ls|1; if (pos<=mid) update(ls,le,mid,pos,xle,xri); else update(rs,mid+1,ri,pos,xle,xri); } int query(int rt,int le,int ri,int xle,int xri,LL k){ if (le==ri) return le; int mid=(le+ri)>>1,ls=rt<<1,rs=ls|1; LL Rz=PST.query(tr[rs],1,n,xle,xri); if (k<=Rz) return query(rs,mid+1,ri,xle,xri,k); else return query(ls,le,mid,xle,xri,k-Rz); } int main(){ scanf("%d%d",&n,&m); nn=n*2+1; PST.clear(); while (m--){ int op,a,b,c; scanf("%d%d%d%d",&op,&a,&b,&c); if (op==1) update(1,1,nn,c+n+1,a,b); else printf("%d ",query(1,1,nn,a,b,c)-n-1); } return 0; }