題解 洛谷 P4695 【[PA2017]Banany】
阿新 • • 發佈:2020-07-20
考慮用動態點分治來解決像本題這樣帶修的樹上路徑問題。
首先對原樹進行點分治,建出點分樹,在點分樹每個節點上用動態開點線段樹來維護以該節點為起點,到其點分樹子樹中每個節點的利潤。
查詢時只需在點分樹上當前所在節點往上跳父親,在其到點分樹根節點的鏈上的每個節點的線段樹上查詢。跳到一個節點時,線上段樹上查詢除了當前節點的利潤最大值,同時加上其到當前節點的花費。
修改點權只需在點分樹上往上跳父親,線上段樹上單點修改即可。
考慮邊權的修改影響的是當前根所對應的一個子樹,對於邊權的修改,從其兩個端點在點分樹上深度更大,即點分治遞迴層數更深的點開始往上跳父親,每次修改在原樹上以當前節點為根,深度更淺的那個端點。
點分樹上每個節點的線段樹在維護時以節點的 \(dfs\) 序為下標,修改子樹資訊只需線上段樹上區間修改即可。
\(code:\)
#include<bits/stdc++.h> #define maxn 100010 #define maxm 200010 #define maxt 3500010 #define inf 1000000000000000 #define mid ((l+r)>>1) #define mk make_pair using namespace std; typedef long long ll; template<typename T> inline void read(T &x) { x=0;char c=getchar();bool flag=false; while(!isdigit(c)){if(c=='-')flag=true;c=getchar();} while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();} if(flag)x=-x; } int n,q,tree_cnt,dfn_cnt,tot,root,pos=1; int fa[maxn],ma[maxn],siz[maxn],num[maxn],d[maxn]; int rt[maxn],ls[maxt],rs[maxt],in[20][maxn],out[20][maxn]; ll v[maxn],tag[maxt]; bool vis[maxn]; map<pair<int,int>,ll> w; struct edge { int to,nxt; ll v; }e[maxm]; int head[maxn],edge_cnt; void add(int from,int to,ll val) { e[++edge_cnt]=(edge){to,head[from],val}; head[from]=edge_cnt; } struct node { ll val; int id; }t[maxt]; bool operator <(const node &a,const node &b) { if(a.val==b.val) return a.id>b.id; return a.val<b.val; } void pushtag(int cur,ll v) { t[cur].val+=v,tag[cur]=v; } void pushdown(int cur) { if(!tag[cur]) return; pushtag(ls[cur],tag[cur]),pushtag(rs[cur],tag[cur]),tag[cur]=0; } void insert(int l,int r,int pos,ll v,int id,int &cur) { if(!cur) cur=++tree_cnt; if(l==r) { t[cur]=(node){v,id}; return; } if(pos<=mid) insert(l,mid,pos,v,id,ls[cur]); else insert(mid+1,r,pos,v,id,rs[cur]); t[cur]=max(t[ls[cur]],t[rs[cur]]); } void modify(int L,int R,int l,int r,ll v,int &cur) { if(!cur) cur=++tree_cnt; if(L<=l&&R>=r) { pushtag(cur,v); return; } pushdown(cur); if(L<=mid) modify(L,R,l,mid,v,ls[cur]); if(R>mid) modify(L,R,mid+1,r,v,rs[cur]); t[cur]=max(t[ls[cur]],t[rs[cur]]); } node query(int L,int R,int l,int r,int cur) { if(L>R||!cur) return (node){-inf,0}; if(L<=l&&R>=r) return t[cur]; pushdown(cur); if(R<=mid) return query(L,R,l,mid,ls[cur]); if(L>mid) return query(L,R,mid+1,r,rs[cur]); return max(query(L,R,l,mid,ls[cur]),query(L,R,mid+1,r,rs[cur])); } void dfs_root(int x,int fath) { siz[x]=1,ma[x]=0; for(int i=head[x];i;i=e[i].nxt) { int y=e[i].to; if(vis[y]||y==fath) continue; dfs_root(y,x),siz[x]+=siz[y],ma[x]=max(ma[x],siz[y]); } ma[x]=max(ma[x],tot-siz[x]); if(ma[x]<ma[root]) root=x; } void dfs_dis(int x,int fath,ll dis,int id) { in[d[id]][x]=++dfn_cnt,insert(1,num[id],dfn_cnt,v[x]-dis,x,rt[id]); for(int i=head[x];i;i=e[i].nxt) { int y=e[i].to; if(vis[y]||y==fath) continue; dfs_dis(y,x,dis+e[i].v,id); } out[d[id]][x]=dfn_cnt; } void solve(int x,int depth) { int now=tot; d[x]=depth,vis[x]=true,num[x]=now,dfn_cnt=0,dfs_dis(x,0,0,x); for(int i=head[x];i;i=e[i].nxt) { int y=e[i].to; if(vis[y]) continue; root=0,tot=siz[y]; if(siz[y]>siz[x]) tot=now-siz[x]; dfs_root(y,x),fa[root]=x,solve(root,depth+1); } } int main() { read(n),read(q); for(int i=1;i<=n;++i) read(v[i]); for(int i=1;i<n;++i) { int x,y; ll v; read(x),read(y),read(v); add(x,y,v),add(y,x,v); if(x>y) swap(x,y); w[mk(x,y)]=v; } tot=ma[0]=n,dfs_root(1,0),solve(root,1); while(q--) { int opt,x,y,p; ll val,t; node ans=(node){-inf,0}; read(opt); if(opt==1) { read(x),read(val); for(int i=x;i;i=fa[i]) modify(in[d[i]][x],in[d[i]][x],1,num[i],val-v[x],rt[i]); v[x]=val; } else { read(x),read(y),read(val); if(x>y) swap(x,y); t=w[mk(x,y)]-val,w[mk(x,y)]=val; if(d[x]<d[y]) p=x; else p=y; while(p) { if(in[d[p]][x]>in[d[p]][y]) modify(in[d[p]][x],out[d[p]][x],1,num[p],t,rt[p]); else modify(in[d[p]][y],out[d[p]][y],1,num[p],t,rt[p]); p=fa[p]; } } for(int i=pos;i;i=fa[i]) { node now=max(query(1,in[d[i]][pos]-1,1,num[i],rt[i]),query(in[d[i]][pos]+1,num[i],1,num[i],rt[i])); now.val+=query(in[d[i]][pos],in[d[i]][pos],1,num[i],rt[i]).val-v[pos],ans=max(ans,now); } printf("%d ",pos=ans.id); } return 0; }