1 条题解
-
0
自动搬运
来自洛谷,原作者为

gcx114514
你们都有光明的未来搬运于
2025-08-24 22:55:48,当前版本为作者最后更新于2024-11-21 19:31:11,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
很有意思的黑题,每一步都很自然。
思路
首先考虑如何比较两个点的删除时间,发现可以通过比较两个点删除时的票数来进行比较我们设 表示点 被删除时的票数,
那么如果 或者 且 那么 比 先删除。
注意到这是一个树形结构,每个人只会影响其祖先的票数,每个人的初始票数为 ,但是如果 说明 比 先删除,那么 ,可以将儿子按照 值排序,令 表示 排序完成的儿子的 的前缀和,那么我们需要找到 即 ,因为有修改,考虑用平衡树来维护,可以在上面二分得到答案,即维护前缀 的最小值,这样有了 (其中 表示树的深度)的做法。
考虑优化,每次修改是影响点 到根这一条路径,考虑能否进行重剖进行优化。一个常用技巧:考虑对每个点只去维护其轻儿子的平衡树,考虑每次加入重儿子的贡献,在换链时作特殊处理。假设对于对于在轻儿子的平衡树上达到的答案是 ,如果 时可以知道 一定在 删除之前删除。否则应该用 到平衡树上二分得到答案。注意到这与每次更新与 没有关系,这是分段函数,可以用线段树维护,因为我们并没有考虑重儿子的情况,所以查询时要查询从我这开始到重链底部的贡献。现在的复杂度是 的。
::::info[code]
#include <bits/stdc++.h> #define pii pair<int,int> #define pb emplace_back #define mid ((l+r)>>1) #define int long long #define mk make_pair #define rs now<<1|1 #define ls now<<1 #define reaD read #define raed read #define haed head #define cotu cout #define se second #define fi first #define itn int using namespace std; bool Mst; const int Max=2e5+10; const int mod=998244353; const int inf=1e18+10; inline int read(){ int res=0,v=1; char c=getchar(); while(c<'0'||c>'9'){v=(c=='-'?-1:1);c=getchar();} while(c>='0'&&c<='9'){res=(res<<3)+(res<<1)+(c^48);c=getchar();} return res*v; } int b[Max],dp[Max],sumb[Max]; int root[Max],Val[Max],n; struct tree{ int minn,sum,ch[2],key; #define ch(x,i) tr[x].ch[i] #define minn(x) tr[x].minn #define sum(x) tr[x].sum #define key(x) tr[x].key }tr[Max]; struct FHQ{ //平衡树编号与树的编号对应,方便处理 void pushup(int now){ sum(now)=sum(ch(now,0))+b[now]+sum(ch(now,1)); minn(now)=min(minn(ch(now,0)),min(sum(ch(now,0))+dp[now],sum(ch(now,0))+b[now]+minn(ch(now,1)))); } void Split(int now,pii val,int &x,int &y){ if(!now){x=y=0;return;} if(dp[now]>val.fi||(dp[now]==val.fi&&now>val.se)){ x=now; Split(ch(now,1),val,ch(x,1),y); }else{ y=now; Split(ch(now,0),val,x,ch(y,0)); } pushup(now); } int merge(int x,int y){ if(!x||!y)return x|y; if(key(x)>key(y)){ ch(x,1)=merge(ch(x,1),y); pushup(x);return x; }else{ ch(y,0)=merge(x,ch(y,0)); pushup(y);return y; } } int Query(int now,int val){ if(!now)return val; if(minn(ch(now,0))<val)return Query(ch(now,0),val); else if(sum(ch(now,0))+dp[now]<val)return val-sum(ch(now,0)); else return Query(ch(now,1),val-sum(ch(now,0))-b[now]); } void insert(int x,int y){ int xx,yy; Split(root[x],mk(dp[y],y),xx,yy); minn(y)=dp[y],sum(y)=b[y];key(y)=rand(); root[x]=merge(xx,merge(y,yy)); } void Del(int x,int y){ int xx,yy,zz; Split(root[x],mk(dp[y],y),xx,yy); Split(yy,mk(dp[y],y-1),yy,zz);root[x]=merge(xx,zz); } }t1; struct Sg{ int lim,x,y;//小于lim返回x,否则返回y Sg(int lim=0,int x=0,int y=0): lim(lim),x(x),y(y){;} int chk(int z){return z<lim?x:y;} Sg operator +(Sg b){return Sg(lim,b.chk(x),b.chk(y));} }f[Max]; //每个点的函数 struct SGT{ //线段树维护分段函数。 Sg tr[Max<<2]; void pushup(int now){ tr[now]=tr[now<<1|1]+tr[now<<1]; } void Modify(int now,int l,int r,int to,Sg val){ if(l==r){tr[now]=val;return;} if(to<=mid)Modify(ls,l,mid,to,val); else Modify(rs,mid+1,r,to,val); pushup(now); } Sg Query(int now,int l,int r,int L,int R){ if(L<=l&&R>=r)return tr[now]; if(R<=mid)return Query(ls,l,mid,L,R); if(L>mid)return Query(rs,mid+1,r,L,R); return Query(rs,mid+1,r,L,R)+Query(ls,l,mid,L,R); } }t2; struct Node{ int to,next; Node(int to=0,int next=0): to(to),next(next){;} }a[Max<<1]; int head[Max],cnt; void add(int x,int y){ a[++cnt]=Node(y,head[x]); head[x]=cnt; } struct Tree{ int siz,son,dep,fa,id,end,top; #define siz(x) bbb[x].siz #define top(x) bbb[x].top #define son(x) bbb[x].son #define dep(x) bbb[x].dep #define end(x) bbb[x].end #define id(x) bbb[x].id #define fa(x) bbb[x].fa }bbb[Max]; Sg Queryf(int now){ if(!son(now))return Sg(0,0,Val[now]); int val=t1.Query(root[now],Val[now]+sumb[now]); //轻儿子的值。 return Sg(val,val,t1.Query(root[now],Val[now]+sumb[now]-b[son(now)])); } void dfs1(int now,int fa){ siz(now)=1;fa(now)=fa;dep(now)=dep(fa)+1; for(int i=head[now];i;i=a[i].next){ int to=a[i].to; if(to==fa)continue; sumb[now]+=b[to]; dfs1(to,now);siz(now)+=siz(to); son(now)=siz(son(now))>siz(to)?son(now):to; } for(int i=head[now];i;i=a[i].next){ int to=a[i].to; if(to==fa||to==son(now))continue; t1.insert(now,to); } f[now]=Queryf(now);dp[now]=f[now].chk(dp[son(now)]); } int Res=0; void dfs2(int now,int top){ id(now)=++Res;top(now)=top;t2.Modify(1,1,n,id(now),f[now]); if(son(now)){ dfs2(son(now),top);end(now)=end(son(now)); }else end(now)=now; //记录链底 for(int i=head[now];i;i=a[i].next){ int to=a[i].to; if(!id(to))dfs2(to,to); } } void work(int x,int opt=-1){ t1.Del(fa(x),x); if(opt!=-1)b[x]=opt; dp[x]=t2.Query(1,1,n,id(x),id(end(x))).chk(0); t1.insert(fa(x),x); } void Modify(int x){ while(x){ f[x]=Queryf(x);t2.Modify(1,1,n,id(x),f[x]); x=top(x); if(x!=1){work(x);} else return ; x=fa(x); } } bool Med; signed main(){n=read(); n=read();int q=read(); for(int i=2;i<=n;++i)add(read(),i); for(int i=1;i<=n;++i)Val[i]=read(); for(int i=1;i<=n;++i)b[i]=read(); minn(0)=inf; dfs1(1,0); dfs2(1,1); for(int i=1;i<=q;++i){ int opt=read(); if(opt==1){ int p,x,y; p=read(),x=read(),y=read(); Val[p]=x;Modify(p); if(p!=1){ sumb[fa(p)]-=b[p];sumb[fa(p)]+=y; if(p!=son(fa(p)))work(p,y); b[p]=y; Modify(fa(p)); } }else{ int x,y; x=read(),y=read(); int tag=x>y; x=t2.Query(1,1,n,id(x),id(end(x))).chk(0); y=t2.Query(1,1,n,id(y),id(end(y))).chk(0); if(x>y||((x==y)&&(tag)))cout << 0 << "\n"; else cout << 1 << "\n"; } } cerr<< "Time: "<<clock()/1000.0 << "s\n"; cerr<< "Memory: " << (&Med-&Mst)/1000000.0 << "MB\n"; return 0; }::::
- 1
信息
- ID
- 9852
- 时间
- 2000ms
- 内存
- 512MiB
- 难度
- 7
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者