Luogu P2726 [SHOI2005]树的双中心

https://www.luogu.com.cn/problem/P2726

题面


\(N\leq 50000,H\leq 100\)

分析

考虑性质:任何一对\((x,y)\),可以把一棵树划分成两部分,其中一部分到\(x\)的距离小,另一部分到\(y\)的距离小
所以枚举每一条边,将其断掉,然后求出两个联通块的最小值加起来取\(Min\)(如果\(x\)的联通块中的点到\(y\)的距离小,则存在一种更优的划分方式)

考虑快速求最小值(重心),令\(f_u\)表示u节点的最小值
\(\forall u=Fa_v ,f_v=f_u+siz_r-2\times siz_v\)
如果\(v\)\(u\)优,则\(siz_v\times 2>siz_r\),只可能是\(u\)中的最大儿子;否则\(u\)最优
而每次断边,只会影响点到根上的值,所以维护最大值和次大值,暴力修改即可

#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int N=1e5+5;
int n,cnt,to[N],nxt[N],he[N],d0[N],d1[N],Fa[N],a[N];
ll siz[N],f[N],g[N],ans;


inline void add(int u,int v) {
	to[++cnt]=v,nxt[cnt]=he[u],he[u]=cnt;
}

void dfs(int fa,int u) {
	Fa[u]=fa;
	siz[u]=a[u]; 
	for(int e=he[u];e;e=nxt[e]) {
		int v=to[e];
		if(v!=fa){
			dfs(u,v);
			f[u]+=f[v]+siz[v];
			siz[u]+=siz[v];
			if(siz[d0[u]]<siz[v]) {
				d1[u]=d0[u],d0[u]=v;
			} else {
				if(siz[d1[u]]<siz[v]) {
					d1[u]=v;
				}
			}
		}
	}
}

void dgs(int u,int r) {
	if(!d0[u]) {
		ans+=g[u];
		return;
	}
	if(siz[d0[u]]>=siz[d1[u]]&&siz[r]<siz[d0[u]]*2) {
		g[d0[u]]=g[u]-siz[d0[u]]*2+siz[r];
		dgs(d0[u],r);
		return;	
	}
	if(siz[d1[u]]>=siz[d0[u]]&&siz[r]<siz[d1[u]]*2) {
		g[d1[u]]=g[u]-siz[d1[u]]*2+siz[r];
		dgs(d1[u],r);
		return;
	}
	ans+=g[u];
}

struct A{int x; ll y,z; };
vector<A>V;

int main() {
	scanf("%d",&n);
	for(int i=1;i<n;i++) {
		int u,v; scanf("%d%d",&u,&v);
		add(u,v),add(v,u);
	}
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	dfs(0,1);
	ll Ans=1e18;
	for(int i=2;i<=n;i++) {
		ans=0;
		g[i]=f[i],dgs(i,i);
		V.clear();
		int u=i;
		while(u!=0) {
			V.push_back((A){u,siz[u],f[u]});
			u=Fa[u];
		}
		for(int j=V.size()-1;j>0;j--) {
			f[V[j].x]-=f[V[j-1].x]+siz[V[j-1].x];
			siz[V[j].x]-=siz[i];
		}
		f[V[0].x]=0,siz[V[0].x]=0;
		for(int j=1;j<V.size();j++) {
			f[V[j].x]+=f[V[j-1].x]+siz[V[j-1].x];
		}
		g[1]=f[1],dgs(1,1);
		for(int j=0;j<V.size();j++) {
			f[V[j].x]=V[j].z,siz[V[j].x]=V[j].y;
		}
	/*	printf("%lld\n",ans);
		for(int j=1;j<=n;j++) {
			printf("%lld ",g[i]);
		}
		puts("");*/
		Ans=min(Ans,ans);
	}
	printf("%lld\n",Ans);
	return 0;
}
posted @ 2020-12-02 21:06  wwwsfff  阅读(76)  评论(0)    收藏  举报