1. 程式人生 > 其它 >P3345 [ZJOI2015]幻想鄉戰略遊戲 題解

P3345 [ZJOI2015]幻想鄉戰略遊戲 題解

題面

\(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;
}