P3345 [ZJOI2015]幻想鄉戰略遊戲 題解
阿新 • • 發佈:2022-05-11
設 \(sum_u\) 為 \(u\) 子樹內 \(d\) 的和,\(sum_d\) 和 \(sum_ch\) 意義是常見點分樹容斥,記錄子樹內 \(d\times dist\) 的和。
首先注意一個性質:假設當前答案為 \(u\),那麼如果 \(u\) 的某個兒子 \(v\) 的答案更優,那麼有 \(sum_v<sum_u-sum_v\),由於 \(2sum_v<sum_u\) 的 \(v\) 只有一個,所以我們可以直接在點分樹上暴力找答案(樹高 \(\log\)),比較答案的過程中,暴力跳點分樹上的父親求即可。複雜度 \(O(20n\log^2 n)\)。
點選檢視程式碼
const int N=1e5+13; struct Edge{int v,w,nxt;}e[N<<1]; int n,h[N],etot; inline void add_edge(int u,int v,int w){e[++etot]=(Edge){v,w,h[u]};h[u]=etot;} namespace Tree{ int fa[N],dep[N],siz[N],son[N],top[N],dis[N]; void dfs1(int u,int f,int deep){ dep[u]=deep,siz[u]=1,fa[u]=f; for(int i=h[u];i;i=e[i].nxt){ int v=e[i].v;if(v==f) continue; dis[v]=dis[u]+e[i].w; dfs1(v,u,deep+1); siz[u]+=siz[v]; if(siz[v]>siz[son[u]]) son[u]=v; } } void dfs2(int u,int topf){ top[u]=topf; if(!son[u]) return; dfs2(son[u],topf); for(int i=h[u];i;i=e[i].nxt){ int v=e[i].v; if(v!=fa[u]&&v!=son[u]) dfs2(v,v); } } inline void init(){dfs1(1,0,0);dfs2(1,1);} inline int lca(int u,int v){ while(top[u]!=top[v]){ if(dep[top[u]]<dep[top[v]]) swap(u,v); u=fa[top[u]]; } return dep[u]<dep[v]?u:v; } inline int dist(int u,int v){return dis[u]+dis[v]-2*dis[lca(u,v)];} } int maxx[N],siz[N],rt,psum,dis[N][21],fa[N],dep[N],from[N]; ll sum[N],sumd[N],sumch[N]; bool vis[N]; void findrt(int u,int f){ siz[u]=1,maxx[u]=0; for(int i=h[u];i;i=e[i].nxt){ int v=e[i].v;if(v==f||vis[v]) continue; findrt(v,u); siz[u]+=siz[v]; maxx[u]=max(maxx[u],siz[v]); } maxx[u]=max(maxx[u],psum-siz[u]); if(maxx[u]<maxx[rt]) rt=u; } void dfs(int u,int f,int topf){ from[u]=topf; for(int i=h[u];i;i=e[i].nxt){ int v=e[i].v; if(v!=f&&!vis[v]) dfs(v,u,topf); } } void build(int u){ vis[u]=1; for(int i=h[u];i;i=e[i].nxt){ int v=e[i].v;if(vis[v]) continue; rt=0,psum=siz[v]; findrt(v,0),findrt(rt,0); dfs(v,0,rt); fa[rt]=u,dep[rt]=dep[u]+1; build(rt); } } inline ll calc(int x){ ll res=sumch[x]; for(int p=x;fa[p];p=fa[p]){ int dd=dis[x][dep[x]-dep[fa[p]]]; res+=(ll)dd*(sum[fa[p]]-sum[p])+(sumch[fa[p]]-sumd[p]); } return res; } ll query(int u){ ll tmp=calc(u); for(int i=h[u];i;i=e[i].nxt){ int v=e[i].v; if(calc(v)<tmp) return query(from[v]); } return tmp; } int main(){ int q;read(n),read(q); for(int i=1;i<n;++i){ int u,v,w;read(u),read(v),read(w); add_edge(u,v,w),add_edge(v,u,w); } maxx[rt=0]=INF,psum=n; findrt(1,0),findrt(rt,0);int RT=rt; build(rt); Tree::init(); for(int i=1;i<=n;++i) for(int j=fa[i];j;j=fa[j]) dis[i][dep[i]-dep[j]]=Tree::dist(i,j); while(q--){ int x,y;read(x),read(y); sum[x]+=y; for(int p=x;fa[p];p=fa[p]){ int dd=dis[x][dep[x]-dep[fa[p]]]; sum[fa[p]]+=y,sumch[fa[p]]+=(ll)y*dd,sumd[p]+=(ll)y*dd; } println(query(RT)); } return 0; }