4.15Codeforces Round #635 (Div. 2)C.Linova and Kingdom题解

题目链接:https://codeforces.ml/contest/1337/problem/C

大意:给出n,k和一个点数量为n的树,让其中k个结点变为工业城市,其余为旅游城市。而每个工业城市到根节点1的路径上存在的旅游城市数量之和求最大,并输出最大值。

样例:
Examples
inputCopy
7 4
1 2
1 3
1 4
3 5
3 6
4 7
outputCopy
7
inputCopy
4 1
1 2
1 3
2 4
outputCopy
2
inputCopy
8 5
7 5
1 7
6 1
3 7
8 3
2 1
4 5
outputCopy
9

这个题我们可以先不考虑两个工业城市连成一个线,让他们对答案的贡献都不变。

以样例一为例子:

我们什么都不管就xjb往深度高的结点去贪心看看:可以,得出的是7。但是我们贪心取得是当前贡献最大,所以先贪567,他们三个用完再去考虑深度为2的234.

如果我们用样例3为例子,就会发现一个问题:
4.15Codeforces Round #635 (Div. 2)C.Linova and Kingdom题解_第1张图片

这是第三样例的图,我们可以看到他们的深度d[i],从1到8分别是:

i d[i]
1 0
2 1
3 2
4 3
5 2
6 1
7 1
8 3

从贪心的想法是:先要了4和8,ans现在等于6了,如果现在我们选了5会怎么样?会导致4的贡献-1,应该说:选了5,5后面有多少个点贡献就会减一。

为什么呢?因为是贪心深度高的点,既然已经选到了5这个点,那么必定他后面的点都已经选完了才会去选他的。

所以现在5这个点的贡献还是不是2?不是了,他的贡献已经是一了。

贪心结论(策略):
深度d[i]-=后面的点down[i];
sort(d);
取前k大。

代码:

//觉得有用的麻烦点个赞呗
typedef unsigned long long ULL;
typedef long long LL;
struct note
{
	int from,to;
};
vector<int >point[200001];
LL d[200001];
int down[200001];
void dfs(int a,int last)
{
	int len=point[a].size();
	for(int time=0;time<len;time++)
	{
		if(point[a][time]==last)continue;
		d[point[a][time]]=d[a]+1;
		dfs(point[a][time],a);
	}
}
void look(int a,int last)
{
	int len=point[a].size();
	int res=len;
	if(a!=1)res--;
	for(int time=0;time<len;time++)
	{
		if(point[a][time]==last)continue;
		look(point[a][time],a);
		res+=down[point[a][time]];
	}
	down[a]=res;
}
int main()
{
	int n,k;
	cin>>n>>k;
	for(int time=1;time<n;time++)
	{
		int from,to;
		cin>>from>>to;
		point[from].push_back(to);
		point[to].push_back(from);
	}
	d[1]=0;
	dfs(1,1);
	look(1,1);
	for(int time=1;time<=n;time++)
	{
		d[time]-=down[time];
	}
	sort(d+1,d+n+1);
	LL ans=0;
	for(int time=n;time>n-k;time--)ans+=d[time];
	printf("%lld\n",ans);
	return 0;
}
//觉得有用的麻烦点个赞呗

还要注意一个问题:ans要开LL,不然会爆炸哦。

你可能感兴趣的:(Div题解)