BZOJ4867 : [Ynoi2017]舌尖上的由乃
首先通过DFS序将原问题转化为序列上区间加、询问区间kth的问题。
考虑分块,设块大小为$K$,每块维护排序过后的$pair(值,编号)$。
对于修改,整块的部分可以直接打标记,而零碎的两块因为本来有序,故可以按照修改区间将其分离成两个有序序列$A$(不在修改区间)和$B$(在修改区间)。
对$B$每个值都加上一个常数,再与$A$归并排序,即可在$O(K)$的时间内修改零碎的两块。
对于查询,首先将零碎的两块用同样的方法分离出来,然后二分答案,在每个整块二分查找个数即可,时间复杂度$O(\frac{n}{K}\log^2n)$。
当$K$取$\sqrt{n}\log n$时取得最优复杂度$O(n\sqrt{n}\log n)$。
#include<cstdio> #include<algorithm> using namespace std; typedef pair<int,int>P; const int N=100010,K=12,M=(N>>K)+5,BUF=6000000; char Buf[BUF],*buf=Buf; int n,m,i,lim,op,x,y,g[N],w[N],nxt[N],stq[N],enq[N],dfn; int block,st[M],en[M],tag[M];P a[N<<1]; inline void read(int&a){for(a=0;*buf<48;buf++);while(*buf>47)a=a*10+*buf++-48;} inline void add(int x,int y,int z){w[y]=z;nxt[y]=g[x];g[x]=y;} void dfs(int x,int y){ stq[x]=++dfn; a[dfn]=P(y,dfn); for(int i=g[x];i;i=nxt[i])dfs(i,y+w[i]); enq[x]=dfn; } inline void change(int o,int l,int r,int p){ static P A[N],B[N]; int ca=0,cb=0,i,j,k=st[o]; for(i=st[o];i<=en[o];i++)if(a[i].second<l||a[i].second>r)A[++ca]=a[i];else B[++cb]=a[i],B[cb].first+=p; i=j=1; while(i<=ca&&j<=cb)a[k++]=A[i]<B[j]?A[i++]:B[j++]; while(i<=ca)a[k++]=A[i++]; while(j<=cb)a[k++]=B[j++]; } inline void modify(int x,int y,int p){ int X=x>>K,Y=y>>K,i; for(i=X+1;i<Y;i++)tag[i]+=p; change(X,x,y,p); if(X<Y)change(Y,x,y,p); } inline int ask(int o,int p){ int l=st[o],r=en[o],mid,t=l-1; if(l>r)return 0; p-=tag[o]; while(l<=r)if(a[mid=(l+r)>>1].first<=p)l=(t=mid)+1;else r=mid-1; return t-st[o]+1; } inline int kth(int x,int y,int k){ if(k>y-x+1)return -1; int X=x>>K,Y=y>>K,i,s=n+1,e=n; tag[block+1]=tag[X]; for(i=st[X];i<=en[X];i++)if(a[i].second>=x&&a[i].second<=y)a[++e]=a[i]; st[block+1]=s,en[block+1]=e; s=e+1; if(X<Y){ tag[block+2]=tag[Y]; for(i=st[Y];i<=en[Y];i++)if(a[i].second<=y)a[++e]=a[i]; } st[block+2]=s,en[block+2]=e; int l=0,r=lim,mid,t,ans; while(l<=r){ mid=(l+r)>>1; t=ask(block+1,mid)+ask(block+2,mid); for(i=X+1;i<Y&&t<k;i++)t+=ask(i,mid); if(t>=k)r=(ans=mid)-1;else l=mid+1; } return ans; } int main(){ fread(Buf,1,BUF,stdin);read(n),read(m),read(x); for(i=2;i<=n;i++)read(x),read(y),add(x,i,y),lim+=y; dfs(1,0); block=n>>K; for(i=1;i<=n;i++)en[i>>K]=i; for(i=n;i;i--)st[i>>K]=i; for(i=0;i<=block;i++)sort(a+st[i],a+en[i]+1); while(m--){ read(op),read(x),read(y); if(op==1)printf("%d\n",kth(stq[x],enq[x],y));else modify(stq[x],enq[x],y),lim+=y; } return 0; }