20181011模拟赛 赛后处刑

版权声明: https://blog.csdn.net/qq_40828060/article/details/83013797

今天T1T3巨难写
然而考完一问发现他们并不这么想,心态直接崩了

最小边权和

神题之一
【问题描述】
给你一棵带边权的树,这棵树是某个完全图唯一的最小生成树。问原来的完
全图中所有边可能的最小边权和是多少。完全图是任意两个点之间都有边相连
的图。
【输入格式】
输出第一行包含一个整数 T 表示数据组数。
每组数据第一个整 N 表示点数。
接下来 N − 1 行,每行三个整数 ai, bi, wi,表示最小生成树上 ai 和 bi 之间
有一条权值为 wi 的边。
【输出格式】
输出 T 行,每行表示一组数据的答案。
【样例输入】
2
3
1 2 4
2 3 7
4
1 2 1
1 3 1
1 4 2
【样例输出】
19
12
【数据规模与约定】
对于 20% 的数据,有 T ≤ 5, N ≤ 5, wi ≤ 5
对于另外 30% 的数据,有 N ≤ 1000,给定的树是一条链
对于 100% 的数据,有 T ≤ 10, N ≤ 20000, wi ≤ 10000

这道题提供了一种思路
即用每条边对其所连的联通块产生的影响来统计答案

50%的数据可以直接枚举点对,找链接两点的树链上最大的边

我们思考30%的数据,把一条链上的一条边单独拿出来,左边的点与右边的点(包括边上的两个点)构成的树链一定经过该边,经过树链的个数为左边所有的点*右边所有的点-1(-1是指去除该边的影响),将其看做联通块然后扩展到正常的树,我们就可以统计该边对答案的贡献了

然而这时有前提的,该边对答案造成影响当且仅当该边是两个联通块的所有边中最大的,显然我们只需将边权从小到大排序即可保证新加入的点一定有贡献,再用并查集维护联通块即可
答案统计:
ans+=(size[fu]*size[fv]-1)*(e[i].w+1);
ans+=MST;

以下是没有什么卵用的东西
我们发现
把-1拿出来
ans+=(size[fu]*size[fv])*(e[i].w+1)-1-Σ(i=1,i<n)e[i].w
Σ(i=1,i<n)e[i].w=MST
所以
ans+=(size[fu]*size[fv])*(e[i].w+1)-1
好像真的没什么卵用

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
typedef unsigned long long ull;
int const maxn=20100,maxm=20099;
struct RE
{
	int u,v,w;
}e[maxm];
int cmp(RE x,RE y)
{
	return x.w<y.w;
}
int n,t,fa[maxn],size[maxm];
ull ans;
int find(int x)
{
	return x==fa[x]?x:fa[x]=find(fa[x]);
}
int main()
{
//	freopen("tree.in","r",stdin);
	scanf("%d",&t);
	while(t--)
	{
		memset(e,0,sizeof(e));
		ans=0;
		scanf("%d",&n);
		for(int i=1;i<n;i++)
			scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w),fa[i]=i,size[i]=1;
		fa[n]=n,size[n]=1;
		//注意细节
		std::sort(e+1,e+n,cmp);
		for(int i=1;i<n;i++)
		{
			int u=find(e[i].u),v=find(e[i].v),w=e[i].w;
			ans+=(ull)size[u]*size[v]*(w+1)-1;
			//中间过程会溢出= =
			fa[u]=v;
			size[v]+=size[u];
		}
		printf("%lld\n",ans);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40828060/article/details/83013797