全局平衡二叉树
全局平衡二叉树,其实说白了就是在树链剖分的基础上再次对每条链以相对平衡的方法再次重构成一颗固态的二叉树的形态,或者说在 LCT 的基础上把 Splay 换成满足全局平衡的二叉树,然后每步暴力往上跳即可。
以下是详细思想。
考虑 LCT 在静态树上常数很大,很大程度上是因为其 splay/access/makeroot
次数太多了。
如果我们能在 LCT 中按照一种合适的方法重建 Splay 的结构,把 Splay 换成某种静态的二叉树结构,则不用做 splay/access/makeroot
操作了,直接暴力跳父亲即可。
怎么换呢?
一种简单的想法是把每条链重构成一颗单独的线段树/静态平衡 BST。
这个东西其实就和普通的树剖区别不大了……单点修改/链查询复杂度容易发现是单次 的。
为什么这个东西是两只老哥的呢?
因为在每条链上,平均每个节点的高度是 的,而且这个东西可以被毒瘤出题人把 条取满老哥的链首尾相接成链然后询问……
考虑如何避免出现这种几个取满老哥的位置首尾相接的毒瘤情况。
注意到平衡二叉树构造时是直接取中点拆成左右两段区间递归下去的,我们觉得这不好;一部分节点子树很大,你应该在这个点这里把它挪的高一点,避免在这里取满老哥。
考虑到中点其实就是一条链的重心,我们要把子树大小作为一个决定谁高谁低的指标的话,就应当求这条链的带权重心;原来求出的平衡二叉树就是一条链的点分树,而我们需要的全局平衡二叉树就是一条链的带权点分树,其中每个节点所在的子树对应重链上的一段区间。这点就和 Splay 维护 LCT 类似了。
因此算法流程就出来了:
- 进行轻重链剖分,把每条链提取出来。
- 对每个结点按轻子树大小之和定点权。
- 对每条重链,求出其带权点分树,即全局平衡二叉树。
- 对每条轻边,按类似 LCT 的方式,儿子认父亲,父亲不认儿子。
- 每条链的信息在平衡树上维护,单点修改/链查询直接暴力跳就好了。
这样,整个算法就完成啦!
容易证明,在这种结构上,树的高度是 的,于是就完了。
当然,在随机数据下,这个算法实际运行效率和树剖差别不大……甚至有的时候常数还更大……
不过,如果使用这个算法来分治,我们就可以叫做树的链分治了。
习题:
- Luogu P4211 [LNOI2014]LCA
- Luogu P4751 "动态DP"&动态树分治(加强版)
- Luogu P3781 [SDOI2017]切树游戏
- ABC269H Antichain
- uoj#23 【UR #1】跳蚤国王下江南
- uoj#801 【统一省选2023】人员调度
- uoj#268 【清华集训2016】数据交互
代码咕咕。
动态树分治那题实现了一下。
下面是未经卡常的慢爆了的代码。
Code
#include <algorithm> #include <stdio.h> #include <vector> typedef long long llt; typedef unsigned uint;typedef unsigned long long ullt; typedef bool bol;typedef char chr;typedef void voi; typedef double dbl; template<typename T>bol _max(T&a,T b){return(a<b)?a=b,true:false;} template<typename T>bol _min(T&a,T b){return(b<a)?a=b,true:false;} template<typename T>T lowbit(T n){return n&-n;} template<typename T>T gcd(T a,T b){return b?gcd(b,a%b):a;} template<typename T>T lcm(T a,T b){return(a!=0||b!=0)?a/gcd(a,b)*b:(T)0;} template<typename T>T exgcd(T a,T b,T&x,T&y){if(b!=0){T ans=exgcd(b,a%b,y,x);y-=a/b*x;return ans;}else return y=0,x=1,a;} template<typename T>T power(T base,T index,T mod) { T ans=1%mod; while(index) { if(index&1)ans=ans*base%mod; base=base*base%mod,index>>=1; } return ans; } llt inf=1e18; struct Mat{ llt A[2][2]; Mat():A{{-inf,-inf},{-inf,-inf}}{} llt*operator[](uint n){return A[n];} friend Mat operator*(Mat a,Mat b){ Mat ans; for(uint i=0;i<2;i++)for(uint j=0;j<2;j++)for(uint k=0;k<2;k++)_max(ans[i][k],a[i][j]+b[j][k]); return ans; } }; uint Fath[1000005],Son[2][1000005]; std::vector<uint>Way[1000005]; uint Siz[1000005],Heavy[1000005],Siz2[1000005]; voi dfs(uint p,uint f){ Siz[p]=1,Fath[p]=f,Heavy[p]=-1; for(auto s:Way[p])if(s!=f){dfs(s,p),Siz[p]+=Siz[s];if(!~Heavy[p]||Siz[s]>Siz[Heavy[p]])Heavy[p]=s;} Siz2[p]=Siz[p];if(~Heavy[p])Siz2[p]-=Siz[Heavy[p]]; } llt G[1000005],H[1000005],V[1000005]; Mat W[1000005]; voi pushup(uint p){ W[p][0][0]=W[p][0][1]=G[p]; W[p][1][0]=H[p],W[p][1][1]=-inf; if(~Son[0][p])W[p]=W[Son[0][p]]*W[p]; if(~Son[1][p])W[p]=W[p]*W[Son[1][p]]; } uint build(uint l,uint r,std::vector<uint>&Ps){ if(l>=r)return-1; uint siz=0,w=-1,wp=l; for(uint i=l;i<r;i++)siz+=Siz2[Ps[i]]; for(uint i=l,s=0;i<r;i++){ uint a=std::max(s,siz-s-Siz2[Ps[i]]); if(_min(w,a))wp=i; s+=Siz2[Ps[i]]; } if(~(Son[0][Ps[wp]]=build(l,wp,Ps)))Fath[Son[0][Ps[wp]]]=Ps[wp]; if(~(Son[1][Ps[wp]]=build(wp+1,r,Ps)))Fath[Son[1][Ps[wp]]]=Ps[wp]; pushup(Ps[wp]);return Ps[wp]; } uint build(uint p){ std::vector<uint>A;while(~p)A.push_back(p),p=Heavy[p]; for(auto p:A)for(auto s:Way[p])if(s!=Fath[p]&&s!=Heavy[p]){ uint a=build(s);Fath[a]=p; G[p]+=std::max(W[a][0][0],W[a][1][0]),H[p]+=W[a][0][0]; } return build(0,A.size(),A); } voi remove(uint p){ std::vector<uint>A; while(~p){ if(~Fath[p]&&p!=Son[0][Fath[p]]&&p!=Son[1][Fath[p]]) G[Fath[p]]-=std::max(W[p][0][0],W[p][1][0]),H[Fath[p]]-=W[p][0][0]; p=Fath[p]; } } voi update(uint p){ while(~p){ pushup(p); if(~Fath[p]&&p!=Son[0][Fath[p]]&&p!=Son[1][Fath[p]]) G[Fath[p]]+=std::max(W[p][0][0],W[p][1][0]),H[Fath[p]]+=W[p][0][0]; p=Fath[p]; } } int main() { #ifdef MYEE freopen("QAQ.in","r",stdin); #endif uint n,m;scanf("%u%u",&n,&m); for(uint i=0;i<n;i++)scanf("%lld",V+i),H[i]=V[i]; for(uint i=1,u,v;i<n;i++)scanf("%u%u",&u,&v),Way[--u].push_back(--v),Way[v].push_back(u); dfs(0,-1); uint rot=build(0); Fath[rot]=-1; llt ans=0; while(m--){ uint p;llt v;scanf("%u%lld",&p,&v),p=(p^ans)-1; remove(p); H[p]-=V[p],H[p]+=V[p]=v; update(p); printf("%lld\n",ans=std::max(W[rot][0][0],W[rot][1][0])); } return 0; }
实现了一下切树游戏。
题解口胡一下。
我们先套路的 FWT 一下。
这样我们只用求出 个对应点的答案。
设 表示以当前点为根的联通子树的权值之和。
设 表示以当前点为根的子树中的联通子树的权值之和。
则 。
设 。
则 。
于是写成矩阵形式。
直接上全局平衡二叉树维护就好了。
值得注意的是,在链顶轻儿子信息传递时, 的维护不能直接乘除,否则可能除零。正确的做法是维护有几个零,以及其余数的乘积。
以下是经过精细卡常的代码。然而还是跑得很慢。
Code
#include <algorithm> #include <stdio.h> #include <vector> typedef long long llt; typedef unsigned uint;typedef unsigned long long ullt; typedef bool bol;typedef char chr;typedef void voi; typedef double dbl; template<typename T>bol _max(T&a,T b){return(a<b)?a=b,true:false;} template<typename T>bol _min(T&a,T b){return(b<a)?a=b,true:false;} template<typename T>T lowbit(T n){return n&-n;} template<typename T>T gcd(T a,T b){return b?gcd(b,a%b):a;} template<typename T>T lcm(T a,T b){return(a!=0||b!=0)?a/gcd(a,b)*b:(T)0;} template<typename T>T exgcd(T a,T b,T&x,T&y){if(b!=0){T ans=exgcd(b,a%b,y,x);y-=a/b*x;return ans;}else return y=0,x=1,a;} template<typename T>T power(T base,T index,T mod) { T ans=1%mod; while(index) { if(index&1)ans=ans*base%mod; base=base*base%mod,index>>=1; } return ans; } const uint Mod=10007; typedef std::vector<uint>modvec; const uint M=128; inline uint chg(uint a){return a>=Mod?a-Mod:a;} voi FWT(modvec&A,bol op){ for(uint i=1;i<M;i<<=1)for(uint j=0;j<M;j++)if(j&i){uint u=A[i^j],t=A[j];A[i^j]=chg(u+t),A[j]=chg(Mod+u-t);} if(op){ uint w=power(M,Mod-2,Mod); for(uint i=0;i<M;i++)(A[i]*=w)%=Mod; } } typedef std::pair<uint,uint>num; uint get(num v){return v.second?0:v.first;} voi insert(num&v,uint w){if(!w)v.second++;else(v.first*=w)%=Mod;} uint Inv[200005]; voi erase(num&v,uint w){if(!w)v.second--;else(v.first*=Inv[w])%=Mod;} typedef std::vector<num>numvec; voi insert(numvec&v,modvec w){for(uint i=0;i<M;i++)insert(v[i],w[i]);} voi erase(numvec&v,modvec w){for(uint i=0;i<M;i++)erase(v[i],w[i]);} const modvec zero(M),one(M,1); struct Mat{ modvec A00,A02,A10,A12; Mat(){A00=A02=A10=A12=zero;} friend Mat operator*(Mat a,Mat b){ Mat ans; for(uint i=0;i<M;i++) ans.A00[i]=a.A00[i]*b.A00[i]%Mod, ans.A02[i]=(a.A00[i]*b.A02[i]+a.A02[i])%Mod, ans.A10[i]=(a.A10[i]*b.A00[i]+b.A10[i])%Mod, ans.A12[i]=(a.A10[i]*b.A02[i]+b.A12[i]+a.A12[i])%Mod; return ans; } }; uint Fath[30005],Son[2][30005]; std::vector<uint>Way[30005]; uint Siz[30005],Heavy[30005],Siz2[30005]; voi dfs(uint p,uint f){ Siz[p]=1,Fath[p]=f,Heavy[p]=-1; for(auto s:Way[p])if(s!=f){dfs(s,p),Siz[p]+=Siz[s];if(!~Heavy[p]||Siz[s]>Siz[Heavy[p]])Heavy[p]=s;} Siz2[p]=Siz[p];if(~Heavy[p])Siz2[p]-=Siz[Heavy[p]]; } numvec H[30005];modvec U[30005],V[30005]; Mat W[30005]; voi pushup(uint p){ for(uint i=0;i<M;i++) W[p].A12[i]=chg(U[p][i]+(W[p].A00[i]=W[p].A02[i]=W[p].A10[i]=get(H[p][i]))); if(~Son[0][p])W[p]=W[Son[0][p]]*W[p]; if(~Son[1][p])W[p]=W[p]*W[Son[1][p]]; } uint build(uint l,uint r,std::vector<uint>&Ps){ if(l>=r)return-1; uint siz=0,w=-1,wp=l; for(uint i=l;i<r;i++)siz+=Siz2[Ps[i]]; for(uint i=l,s=0;i<r;i++){ uint a=std::max(s,siz-s-Siz2[Ps[i]]); if(_min(w,a))wp=i; s+=Siz2[Ps[i]]; } if(~(Son[0][Ps[wp]]=build(l,wp,Ps)))Fath[Son[0][Ps[wp]]]=Ps[wp]; if(~(Son[1][Ps[wp]]=build(wp+1,r,Ps)))Fath[Son[1][Ps[wp]]]=Ps[wp]; pushup(Ps[wp]);return Ps[wp]; } uint build(uint p){ std::vector<uint>A;while(~p)A.push_back(p),p=Heavy[p]; for(auto p:A)for(auto s:Way[p])if(s!=Fath[p]&&s!=Heavy[p]){ uint a=build(s);Fath[a]=p; for(uint i=0;i<M;i++)insert(H[p][i],chg(W[a].A02[i]+1)),U[p][i]=chg(U[p][i]+W[a].A12[i]); } return build(0,A.size(),A); } voi remove(uint p){ std::vector<uint>A; while(~p){ if(~Fath[p]&&p!=Son[0][Fath[p]]&&p!=Son[1][Fath[p]])for(uint i=0;i<M;i++) erase(H[Fath[p]][i],chg(W[p].A02[i]+1)),U[Fath[p]][i]=chg(U[Fath[p]][i]+Mod-W[p].A12[i]); p=Fath[p]; } } voi update(uint p){ while(~p){ pushup(p); if(~Fath[p]&&p!=Son[0][Fath[p]]&&p!=Son[1][Fath[p]])for(uint i=0;i<M;i++) insert(H[Fath[p]][i],chg(W[p].A02[i]+1)),U[Fath[p]][i]=chg(U[Fath[p]][i]+W[p].A12[i]); p=Fath[p]; } } int main() { #ifdef MYEE freopen("QAQ.in","r",stdin); #endif Inv[1]=1;for(uint i=2;i<Mod;i++)Inv[i]=Inv[Mod%i]*(Mod-Mod/i)%Mod; uint n,q;scanf("%u%*u",&n); for(uint i=0,v;i<n;i++){ scanf("%u",&v);V[i]=U[i]=zero;V[i][v]=1,FWT(V[i],0); H[i].resize(M,{1,0}),insert(H[i],V[i]),Son[0][i]=Son[1][i]=-1u; } for(uint i=1,u,v;i<n;i++)scanf("%u%u",&u,&v),Way[--u].push_back(--v),Way[v].push_back(u); dfs(0,-1);uint rot=build(0); Fath[rot]=-1; scanf("%u",&q); while(q--){ chr C[15];scanf("%s",C); if(*C=='Q'){ uint k;scanf("%u",&k);modvec A=W[rot].A12;FWT(A,1);printf("%u\n",A[k]); }else{ uint p,v;scanf("%u%u",&p,&v),p--; remove(p),erase(H[p],V[p]),V[p]=zero;V[p][v]=1,FWT(V[p],0),insert(H[p],V[p]),update(p); } } return 0; }
跳蚤国王下江南那题,注意到是仙人掌,可以认为是对圆方树进行树剖,然后在链上按照全局平衡二叉树的建法进行分治卷积,复杂度就做到了 。
这种方法通常被称为链分治。
为了保证复杂度,这题需要对方点用一些小技巧计算贡献。
Code
// 多项式码头略去了。 // 完整代码:https://uoj.ac/submission/621021 const ullt Mod=998244353,g=3; typedef ConstMod::mod_ullt<Mod>modint; typedef std::vector<modint>modvec; typedef NTT_POLY::poly_NTT<Mod,g>poly; typedef NTT_POLY::poly_eval<Mod,g>eval; typedef NTT_POLY::poly_inter<Mod,g>inter; typedef NTT_POLY::poly_cpd<Mod,g>cpd; typedef NTT_POLY::poly_nums<Mod,g>nums; typedef FWT_MODINT::FWT_Mod<Mod>FWT; std::vector<uint>Way[100005],Son[200005]; uint Siz[200005],Heavy[200005],n,m,tp; voi dfs(uint p,uint f) { static bol Now[100005]; static uint Dfn[100005],Fath[100005],t=0; if(!~f)for(uint i=0;i<n;i++)Dfn[i]=-1; Now[p]=true,Fath[p]=f,Dfn[p]=t++; for(auto s:Way[p])if(Dfn[s]<Dfn[p]) { if(s==f){f=-1;continue;} uint a=p; std::vector<uint>V; while(a!=s)V.push_back(a),Now[a]=false,Son[tp].push_back(a),a=Fath[a]; Son[s].push_back(tp++); } else if(!~Dfn[s]) { dfs(s,p);if(Now[s])Son[p].push_back(s); } } voi dfs2(uint p) { Siz[p]=1,Heavy[p]=-1; for(auto s:Son[p]) { dfs2(s),Siz[p]+=Siz[s]; if(!~Heavy[p]||Siz[s]>Siz[Heavy[p]])Heavy[p]=s; } } std::pair<poly,poly>solve(uint l,uint r,std::vector<uint>&T, std::vector<poly>&Q,std::vector<poly>&S) { if(l+1==r)return{Q[l],S[l]*Q[l]}; uint p=l,s=0;for(uint i=l;i<r;i++)s+=T[i]; for(uint i=l+1,w=T[l],t=-1;i<r;w+=T[i++])if(_min(t,std::max(w,s-w)))p=i; auto a=solve(l,p,T,Q,S); auto b=solve(p,r,T,Q,S); return{a.first*b.first,a.second+b.second*a.first}; } poly dfs3(uint p,poly P) { std::vector<uint>T;std::vector<poly>Q{P},S; while(~p) { T.push_back(Siz[p]); if(p<n) { S.push_back(modvec{1}); for(auto s:Son[p]) if(s!=Heavy[p])S.back()+=dfs3(s,modvec{0,1}); else Q.push_back(modvec{0,1}); } else { S.push_back(modvec{0}); for(uint i=0;i<Son[p].size();i++)if(Son[p][i]!=Heavy[p]) { auto w=dfs3(Son[p][i],modvec{1}); if(S.back().size()<w.size()+std::max<uint>(i,Son[p].size()-i-1)) S.back().chg_siz(w.size()+std::max<uint>(i,Son[p].size()-i-1)); for(uint j=0;j<w.size();j++) S.back()[j+i]+=w[j],S.back()[j+Son[p].size()-i-1]+=w[j]; } else { Q.push_back(poly(std::max<uint>(i,Son[p].size()-i-1)+1)); Q.back()[i]++,Q.back()[Son[p].size()-i-1]++; } } p=Heavy[p]; } for(uint i=1;i<T.size();i++)T[i-1]-=T[i]; return solve(0,T.size(),T,Q,S).second; } int main() { #ifdef MYEE freopen("QAQ.in","r",stdin); freopen("QAQ.out","w",stdout); #endif scanf("%u%u",&n,&m),tp=n; while(m--) { uint u,v;scanf("%u%u",&u,&v),Way[--u].push_back(--v),Way[v].push_back(u); } dfs(0,-1),dfs2(0); auto t=dfs3(0,modvec{1}); for(uint i=1;i<n;i++)t.val(i).println(); return 0; }
人员调度那题,线段树分治 + 贪心转化后,注意到要实现一个链加链 ,我们直接写个 GBT 就好了。懒标记什么的都打上即可。
对于子树的询问,可以直接 dfn 序线段树维护,不是重点。
Code
// 完整代码:https://uoj.ac/submission/633434 struct Pq { std::priority_queue<uint,std::vector<uint>,std::greater<uint> >Q1,Q2; voi insert(uint w){Q1.push(w);} voi erase(uint w){Q2.push(w);} uint top() { while(Q2.size()&&Q1.top()==Q2.top())Q1.pop(),Q2.pop(); return Q1.size()?Q1.top():-1u; } voi pop(){Q1.pop();} }Q[100005]; std::vector<uint>Son[100005]; uint Fath[100005],Siz[100005],Fath_BST[100005],Son_BST[2][100005]; uint Dfn[100005],Utl[100005],Back[100005],cnt; int MinS[100005],S[100005],Tag[100005]; voi pushdown(uint p) { if(Tag[p]) { if(~Son_BST[0][p])Tag[Son_BST[0][p]]+=Tag[p],MinS[Son_BST[0][p]]+=Tag[p],S[Son_BST[0][p]]+=Tag[p]; if(~Son_BST[1][p])Tag[Son_BST[1][p]]+=Tag[p],MinS[Son_BST[1][p]]+=Tag[p],S[Son_BST[1][p]]+=Tag[p]; Tag[p]=0; } } voi update(uint p) { if(~Fath_BST[p])update(Fath_BST[p]); pushdown(p); } uint find(uint p) { update(p); bol op=true; while(~p) { if(op) { if(!S[p])return p; if(~Son_BST[0][p]&&!MinS[Son_BST[0][p]]){p=Son_BST[0][p];break;} } op=!~Fath_BST[p]||p!=Son_BST[0][Fath_BST[p]],p=Fath_BST[p]; } while(~p) { pushdown(p); if(~Son_BST[1][p]&&!MinS[Son_BST[1][p]]){p=Son_BST[1][p];continue;} if(!S[p])break; p=Son_BST[0][p]; } return p; } voi add(uint p,int v) { update(p); bol op=true; while(~p) { if(op){S[p]+=v;if(~Son_BST[0][p])Tag[Son_BST[0][p]]+=v,MinS[Son_BST[0][p]]+=v,S[Son_BST[0][p]]+=v;} MinS[p]=S[p]; if(~Son_BST[0][p])_min(MinS[p],MinS[Son_BST[0][p]]); if(~Son_BST[1][p])_min(MinS[p],MinS[Son_BST[1][p]]); op=!~Fath_BST[p]||p!=Son_BST[0][Fath_BST[p]],p=Fath_BST[p]; } } uint build_BST(uint o,uint y) { if(o==y)return-1; uint p=o; for(uint q=o,w=-1;q!=y;q=Son[q].size()?Son[q][0]:-1u) if(_min(w,std::max(Siz[o]-Siz[q],(Son[q].size()?Siz[Son[q][0]]:0)-(~y?Siz[y]:0))))p=q; if(~(Son_BST[0][p]=build_BST(o,p)))Fath_BST[Son_BST[0][p]]=p; if(~(Son_BST[1][p]=build_BST(Son[p].size()?Son[p][0]:-1u,y)))Fath_BST[Son_BST[1][p]]=p; return Fath_BST[p]=-1,p; } uint build(uint p) { uint o=p; while(true) { Back[Dfn[p]=cnt++]=p; if(Son[p].size()){ std::sort(Son[p].begin(),Son[p].end(),[&](uint a,uint b){return Siz[a]>Siz[b];}); for(uint i=1;i<Son[p].size();i++)Fath_BST[build(Son[p][i])]=p; p=Son[p][0]; }else break; } for(;;p=Fath[p]){Utl[p]=cnt;if(p==o)break;} return build_BST(o,-1); } namespace Seg { uint MinW[1u<<18|1],P[1u<<18|1]; voi build(uint u,uint l,uint r) { P[u]=Back[l];if(r-l>1)build(u<<1,l,(l+r)>>1),build(u<<1|1,(l+r)>>1,r); } voi chg(uint p,uint w,uint u,uint n) { if(n==1){MinW[u]=w;return;} if(p<(n>>1))chg(p,w,u<<1,n>>1);else chg(p-(n>>1),w,u<<1|1,n-(n>>1)); MinW[u]=MinW[u<<1],P[u]=P[u<<1];if(_min(MinW[u],MinW[u<<1|1]))P[u]=P[u<<1|1]; } voi find(uint l,uint r,uint u,uint n,uint&w,uint&p){ if(w<=MinW[u])return; if(!l&&r==n){w=MinW[u],p=P[u];return;} if(l<(n>>1)) if(r<=(n>>1))find(l,r,u<<1,n>>1,w,p); else find(l,n>>1,u<<1,n>>1,w,p),find(0,r-(n>>1),u<<1|1,n-(n>>1),w,p); else find(l-(n>>1),r-(n>>1),u<<1|1,n-(n>>1),w,p); } } uint P[200005],W[200005],Bgn[200005],End[200005],n,k,m;ullt Ans[100005]; std::vector<uint>SegT[262145]; voi insert(uint l,uint r,uint p,uint u,uint n) { if(!l&&r==n){SegT[u].push_back(p);return;} if(l<(n>>1)) if(r<=(n>>1))insert(l,r,p,u<<1,n>>1); else insert(l,n>>1,p,u<<1,n>>1),insert(0,r-(n>>1),p,u<<1|1,n-(n>>1)); else insert(l-(n>>1),r-(n>>1),p,u<<1|1,n-(n>>1)); } voi dfs(uint u,uint l,uint len,ullt ans) { std::vector<std::pair<std::pair<uint,uint>,std::pair<uint,uint> > >V; std::vector<uint>R; for(auto e:SegT[u]) { uint p=P[e],w=W[e],q,g=-1; q=find(p),Seg::find(Dfn[q],Utl[q],1,n,g,q);if(w<=g)continue; V.push_back({{p,w},{q,g}}),ans+=w-g,Q[q].pop();if(p!=q)add(q,1),add(p,-1); Seg::chg(Dfn[q],Q[q].top(),1,n),R.push_back(q); if(w<Q[p].top())Seg::chg(Dfn[p],w,1,n),R.push_back(p); Q[p].insert(w); } if(len>1)dfs(u<<1,l,len>>1,ans),dfs(u<<1|1,l+(len>>1),len-(len>>1),ans);else Ans[l]=ans; std::reverse(V.begin(),V.end()); for(auto s:V) { Q[s.first.first].erase(s.first.second),Q[s.second.first].insert(s.second.second); if(s.first.first!=s.second.first)add(s.first.first,1),add(s.second.first,-1); } std::sort(R.begin(),R.end()),R.erase(std::unique(R.begin(),R.end()),R.end()); for(auto p:R)Seg::chg(Dfn[p],Q[p].top(),1,n); } int main() { #ifdef MYEE freopen("QAQ.in","r",stdin); freopen("QAQ.out","w",stdout); #endif scanf("%*u%u%u%u",&n,&k,&m),Fath[0]=-1; for(uint i=1;i<n;i++)scanf("%u",Fath+i),Son[--Fath[i]].push_back(i),Siz[i]=1; for(uint i=n-1;i;i--)Siz[Fath[i]]+=Siz[i]; for(uint i=0;i<n;i++)Q[i].insert(0); build(0),Seg::build(1,0,n); for(uint i=0;i<k;i++)scanf("%u%u",P+i,W+i),P[i]--,End[i]=m+1; for(uint i=1,p;i<=m;i++) { scanf("%u",&p);if(p==1)scanf("%u%u",P+k,W+k),P[k]--,Bgn[k]=i,End[k++]=m+1;else scanf("%u",&p),End[p-1]=i; } for(uint i=0;i<k;i++)insert(Bgn[i],End[i],i,1,m+1); dfs(1,0,m+1,0); for(uint i=0;i<=m;i++)printf("%llu%c",Ans[i]," \n"[i==m]); return 0; }
数据交互那题,我们的 ddp 是简单列出的,主要是怎么维护轻子树信息;再写个 size 平衡的二叉树维护轻子树即可。由于懒,写了 priority_queue
,结果导致总复杂度仍为 。
Code
// 完整代码:https://uoj.ac/submission/629313 struct Info{ llt A01,A02,A11,A12; friend Info operator*(Info a,Info b){ Info ans; ans.A01=std::max(a.A01+b.A11,b.A01); ans.A02=std::max({a.A02,a.A01+b.A12,b.A02}); ans.A11=a.A11+b.A11; ans.A12=std::max({a.A12,a.A11+b.A12}); return ans; } }; std::vector<uint>Way[100005]; uint Fath[100005],Siz[100005],Rot[100005],Dep[100005]; voi dfs(uint p,uint f) { std::vector<uint>S;Siz[p]=1,Fath[p]=f; for(auto s:Way[p])if(s!=f)Dep[s]=Dep[p]+1,dfs(s,p),Siz[p]+=Siz[s],S.push_back(s); std::sort(S.begin(),S.end(),[&](uint a,uint b){return Siz[a]>Siz[b];}),Way[p]=S; } uint Fath_BST[100005],Son_BST[2][100005];Info W_BST[100005];llt Tag_BST[100005]; uint Fath_SonBST[100005],Rot_SonBST[100005],Son_SonBST[2][100005]; llt MaxV_SonBST[100005],SecMaxV_SonBST[100005],A[100005]; voi pushup_BST(uint p) { auto&w=W_BST[p]; w.A11=A[p]; w.A01=w.A12=A[p]+(~Rot_SonBST[p]?MaxV_SonBST[Rot_SonBST[p]]:0); w.A02=A[p]+(~Rot_SonBST[p]?MaxV_SonBST[Rot_SonBST[p]]+SecMaxV_SonBST[Rot_SonBST[p]]:0); if(~Son_BST[0][p])w=W_BST[Son_BST[0][p]]*w; if(Tag_BST[p])w.A01+=Tag_BST[p],w.A02+=Tag_BST[p]; if(~Son_BST[1][p])w=w*W_BST[Son_BST[1][p]]; } uint build_BST(uint o,uint y) { if(o==y)return-1; uint g=-1,m=-1; for(uint p=o;p!=y;p=Way[p].size()?Way[p][0]:-1u) if(_min(g,std::max(Siz[o]-Siz[p],(Way[p].size()?Siz[Way[p][0]]:0)-(~y?Siz[y]:0))))m=p; if(~(Son_BST[0][m]=build_BST(o,m)))Fath_BST[Son_BST[0][m]]=m; if(~(Son_BST[1][m]=build_BST(Way[m].size()?Way[m][0]:-1u,y)))Fath_BST[Son_BST[1][m]]=m; pushup_BST(m);Fath_BST[m]=-1;return m; } voi pushup_SonBST(uint p) { MaxV_SonBST[p]=W_BST[p].A12,SecMaxV_SonBST[p]=0; if(~Son_SonBST[0][p]) { if(MaxV_SonBST[Son_SonBST[0][p]]>MaxV_SonBST[p]) _max(SecMaxV_SonBST[p]=MaxV_SonBST[p],SecMaxV_SonBST[Son_SonBST[0][p]]), MaxV_SonBST[p]=MaxV_SonBST[Son_SonBST[0][p]]; else _max(SecMaxV_SonBST[p],MaxV_SonBST[Son_SonBST[0][p]]); } if(~Son_SonBST[1][p]) { if(MaxV_SonBST[Son_SonBST[1][p]]>MaxV_SonBST[p]) _max(SecMaxV_SonBST[p]=MaxV_SonBST[p],SecMaxV_SonBST[Son_SonBST[1][p]]), MaxV_SonBST[p]=MaxV_SonBST[Son_SonBST[1][p]]; else _max(SecMaxV_SonBST[p],MaxV_SonBST[Son_SonBST[1][p]]); } } uint build_SonBST(std::vector<uint>&V,uint l,uint r) { if(l==r)return-1; uint s=0,g=-1,m=-1; for(uint j=l;j<r;j++)s+=Siz[V[j]]; for(uint j=l,t=0;j<r;t+=Siz[V[j++]])if(_min(g,std::max(t,s-=Siz[V[j]])))m=j; if(~(Son_SonBST[0][V[m]]=build_SonBST(V,l,m)))Fath_SonBST[Son_SonBST[0][V[m]]]=V[m]; if(~(Son_SonBST[1][V[m]]=build_SonBST(V,m+1,r)))Fath_SonBST[Son_SonBST[1][V[m]]]=V[m]; pushup_SonBST(V[m]);return V[m]; } std::priority_queue<llt>Q1,Q2; uint dfs2(uint p) { uint r=p; while(~p) { Rot[p]=r,Fath_SonBST[p]=-1; if(Way[p].size()) { std::vector<uint>S;for(uint j=1;j<Way[p].size();j++)S.push_back(dfs2(Way[p][j])),Fath_BST[S.back()]=p; if(~(Rot_SonBST[p]=build_SonBST(S,0,S.size())))Fath_SonBST[Rot_SonBST[p]]=-1; p=Way[p][0]; }else Rot_SonBST[p]=-1,p=-1; } Q1.push(0); return build_BST(r,-1); } uint lca(uint u,uint v) { while(Rot[u]!=Rot[v]) { if(Dep[Rot[u]]<Dep[Rot[v]])std::swap(u,v); u=Fath[Rot[u]]; } return Dep[u]<Dep[v]?u:v; } voi addA(uint p,llt w) { uint q=p; while(~q) { if(!~Fath_BST[q]||(Son_BST[0][Fath_BST[q]]!=q&&Son_BST[1][Fath_BST[q]]!=q)) Q2.push(std::max(W_BST[q].A01,W_BST[q].A02)); q=Fath_BST[q]; } A[p]+=w; while(~p) { pushup_BST(p); if(!~Fath_BST[p]||(Son_BST[0][Fath_BST[p]]!=p&&Son_BST[1][Fath_BST[p]]!=p)) { Q1.push(std::max(W_BST[p].A01,W_BST[p].A02)); q=p;while(~q)pushup_SonBST(q),q=Fath_SonBST[q]; } p=Fath_BST[p]; } } voi addB(uint p,llt w) { uint q=p; while(~q) { if(!~Fath_BST[q]||(Son_BST[0][Fath_BST[q]]!=q&&Son_BST[1][Fath_BST[q]]!=q)) Q2.push(std::max(W_BST[q].A01,W_BST[q].A02)); q=Fath_BST[q]; } bol ok=true; while(~p) { if(ok)Tag_BST[p]+=w; pushup_BST(p); if(!~Fath_BST[p]||(Son_BST[0][Fath_BST[p]]!=p&&Son_BST[1][Fath_BST[p]]!=p)) { Q1.push(std::max(W_BST[p].A01,W_BST[p].A02)),ok=true; q=p;while(~q)pushup_SonBST(q),q=Fath_SonBST[q]; } else ok=p==Son_BST[1][Fath_BST[p]]; p=Fath_BST[p]; } } int main() { #ifdef MYEE freopen("QAQ.in","r",stdin); freopen("QAQ.out","w",stdout); #endif uint n,q;scanf("%u%u",&n,&q); for(uint i=1,u,v;i<n;i++)scanf("%u%u",&u,&v),Way[--u].push_back(--v),Way[v].push_back(u); dfs(0,-1),Fath_BST[dfs2(0)]=-1; for(uint t=0;t<q;t++) { static chr Op[5];static uint U[100005],V[100005];static llt W[100005]; scanf("%s",Op);uint u,v;llt w; if(*Op=='+')scanf("%u%u%lld",&u,&v,&w),U[t]=--u,V[t]=--v,W[t]=w;else scanf("%u",&u),w=-W[--u],v=V[u],u=U[u]; uint r=lca(u,v);addA(r,w),addB(u,w),addB(v,w),addB(r,-2*w); while(Q2.size()&&Q1.top()==Q2.top())Q1.pop(),Q2.pop(); printf("%lld\n",Q1.top()); } return 0; }
本文来自博客园,作者:myee,转载请注明原文链接:https://www.cnblogs.com/myee/p/global-biased-tree.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫