题目大意: 给出一棵树,两个点配对的贡献是两点间的距离,求最大贡献和。
题解
出于某些原因来水一下这题题解,如果你是从超链接来的大概就知道原因了qwq。
思路是贪心,对于每条边,他的贡献次数为 它连接的两棵子树中 较小的子树的大小,显然这是它的最大贡献次数,需要证明的是:总是存在一种方案使得每条边的贡献次数都是最大的。下面给出一种方案:
先找出树的重心作为根,然后给每个点染色,两个点颜色相同当且仅当两个点属于根节点的同一个儿子的子树,特别的,根节点与其他点颜色都不同。
接下来将这些点进行配对,并保证配对的点颜色不同即可。将相同颜色的点放在一起,对于第 i ∈ [ 1 , n 2 ] i\in[1,\dfrac n 2] i∈[1,2n] 个点,将它与第 i + n 2 i+\dfrac n 2 i+2n 个点配对,这样就可以保证配对的点颜色不同,因为树的重心有一个性质:每棵子树大小都不超过 n 2 \dfrac n 2 2n,即每种颜色的点数不超过 n 2 \dfrac n 2 2n,那么第 i i i 个点和第 i + n 2 i+\dfrac n 2 i+2n 个点颜色必定不同。
举个例子,对于这样一棵树:
2
,
3
,
4
2,3,4
2,3,4 的颜色相同,其他点互不相同。
相同颜色的放在一起,就是 1 , 5 , 6 , 2 , 3 , 4 1,5,6,2,3,4 1,5,6,2,3,4, n 2 = 3 \dfrac n 2=3 2n=3,即第 i i i 个数与第 i + 3 i+3 i+3 个数配对, ( 1 , 2 ) , ( 5 , 3 ) , ( 6 , 4 ) (1,2),(5,3),(6,4) (1,2),(5,3),(6,4),这就是最优配对方案,你可以随便填一些边权来手玩一下。
代码如下:
#include <cstdio>
#include <algorithm>
using namespace std;
#define maxn 100010
int n;
struct edge{int y,z,next;}e[maxn<<1];
int first[maxn],len=0;
void buildroad(int x,int y,int z){e[++len]=(edge){y,z,first[x]};first[x]=len;}
int size[maxn];
long long ans=0;
void dfs(int x,int fa){
size[x]=1;
for(int i=first[x],y;i;i=e[i].next)
if((y=e[i].y)!=fa)dfs(y,x),ans+=1ll*e[i].z*min(size[y],n-size[y]),size[x]+=size[y];
}
int main()
{
scanf("%d",&n);for(int i=1,x,y,z;i<n;i++)
scanf("%d %d %d",&x,&y,&z),buildroad(x,y,z),buildroad(y,x,z);
dfs(1,0);printf("%lld",ans);
}