题目链接: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为例子,就会发现一个问题:
这是第三样例的图,我们可以看到他们的深度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,不然会爆炸哦。