LOJ#6046. 「雅礼集训 2017 Day8」爷
Description
给你一个$n$个点的有根树,$1$为根,带边权,有$m$次操作。
求$x$的子树中第$k$小的深度的值,如果子树中没有$k$个点则输出$-1$;
将$x$与$x$父亲的边权加上$k$。
保证每次操作 2 的$k$以及原树的边权小于等于一个数$len$。
如果操作 2 中$x$为$1$,那么视为将$x$的基础深度加上了$k$。
Solution
先用dfs序将其转化为区间上的问题
如果直接分块并在块内排序,每次查找时二分答案再在每个块内二分,复杂度是$O(n \sqrt{n} \log^2 n)$的
题中说边权以及每次加的值不是很大
可以控制每个块内极差在一定范围内,维护块内前缀和,就可以做到查询时复杂度$O(n \sqrt{n} \log n)$
需要定期地对整个序列重建分块
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<utility> #include<cstring> #include<vector> #include<cstdio> #include<cmath> using namespace std; int n,m,K,head[100005],tot,ll[100005],rr[100005],dfn,a[100005],cnt,plz[100005],tik,B,bot[100005],top[100005],sum[1005][16005],bel[100005],bg[100005],ed[100005],sta1[100005],sta2[100005],top1,top2,lim; const int inf=0x7f7f7f7f; struct Edge{ int to,nxt,w; }edge[200005]; inline int read(){ int f=1,w=0; char ch=0; while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9')w=(w<<1)+(w<<3)+ch-'0',ch=getchar(); return f*w; } void solve(int id){ top[id]=-inf,bot[id]=inf; for(int j=bg[id];j<=ed[id];j++)top[id]=max(top[id],a[j]),bot[id]=min(bot[id],a[j]); memset(sum[id],0,(top[id]-bot[id]+1)<<2); for(int j=bg[id];j<=ed[id];j++)++sum[id][a[j]-bot[id]]; for(int j=1;j<=top[id]-bot[id];j++)sum[id][j]+=sum[id][j-1]; } void rebuild(){ for(int i=1;i<=n;i++)a[i]+=plz[bel[i]]; memset(plz,0,(cnt+1)<<2),memset(bg,0,(cnt+1)<<2),cnt=tik=0,cnt=1; int maxx=a[1],minn=a[1],len=0; for(int i=1;i<=n;i++){ ++len,bel[i]=cnt,ed[cnt]=i,maxx=max(maxx,a[i+1]),minn=min(minn,a[i+1]); if(!bg[cnt])bg[cnt]=i; if(maxx-minn+1>=B*K||len>=B||i==n)solve(cnt),maxx=minn=a[i+1],len=0,++cnt; } --cnt; } void dfs(int k,int dep){ ll[k]=++dfn,a[dfn]=dep,lim=max(lim,dep); for(int i=head[k];i;i=edge[i].nxt)dfs(edge[i].to,dep+edge[i].w); rr[k]=dfn; } int calc(int x){ int ret=0; for(int i=1;i<=top1;i++){ if(x<plz[sta1[i]]+bot[sta1[i]])continue; if(x>=plz[sta1[i]]+top[sta1[i]])ret+=ed[sta1[i]]-bg[sta1[i]]+1; else ret+=sum[sta1[i]][x-plz[sta1[i]]-bot[sta1[i]]]; } for(int i=1;i<=top2;i++)ret+=(sta2[i]<=x); return ret; } int query(int l,int r,int k){ top1=top2=0; for(int i=l;i<=r;i=ed[bel[i]]+1) if(i==bg[bel[i]]&&ed[bel[i]]<=r)sta1[++top1]=bel[i]; else for(int j=i;j<=min(r,ed[bel[i]]);j++)sta2[++top2]=a[j]+plz[bel[i]]; int L=0,R=lim,ret=0; while(L<=R){ int mid=L+R>>1; if(calc(mid)>=k)ret=mid,R=mid-1; else L=mid+1; } return ret; } void update(int l,int r,int k){ lim+=k; for(int i=l;i<=r;i=ed[bel[i]]+1) if(i==bg[bel[i]]&&ed[bel[i]]<=r)plz[bel[i]]+=k; else{ for(int j=i;j<=min(r,ed[bel[i]]);j++)a[j]+=k; for(int j=bg[bel[i]];j<=ed[bel[i]];j++)a[j]+=plz[bel[i]]; plz[bel[i]]=0,solve(bel[i]); } } int main(){ n=read(),m=read(),K=read(),B=sqrt(n); for(int i=2;i<=n;i++){ int x=read(),w=read(); edge[++tot]=(Edge){i,head[x],w},head[x]=tot; } dfs(1,0),rebuild(); for(;m;m--){ int opt=read(),x=read(),k=read(); if(opt==1)printf("%d\n",rr[x]-ll[x]+1>=k?query(ll[x],rr[x],k):-1); else{ update(ll[x],rr[x],k),++tik; if(tik>=1000)rebuild(); } } return 0; }