題目鏈接: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 1 2 3 4 5 6 7 8
d[i] 0 1 2 3 2 1 1 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,不然會爆炸哦。