codeforces-#656-F

codeforces-#656-F

七月 22, 2020

F. Removing Leaves

题意有一棵树,共有n个节点 n-1条边

每次从同一个节点上去除k个叶子,问最多去除几次。

题解

贪心的策略是:每次剪掉即可。因为剪去叶子不会对其他的节点产生负影响。也就是

说,剪除一次以后,只会说多出一个节点可以减,不会说剪除了以后导致其他节点减不

了。因此这道题根本不用考虑剪枝的先后问题,直接剪就行了。

具体实现

考虑类比拓扑排序,将叶子结点入队,然后对其相邻结点遍历,当被遍历次数到达k

次,ans++,如果此时度数为1就入队,注意需要用vis数组标记一下那些结点已经入过

队,且标记时需要在queue,一次标记一个,因为存在edge(u,v),这样的情况,度数都

是1,但是如果都标记了,就算不了贡献了

参考博客 参考博客

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define endl '\n'
#define Turnoff std::ios::sync_with_stdio(false)
const ll Max=2e5+5;
vector<int>mp[Max];
int edge[Max],viscnt[Max];
bool vis[Max];
int main() {
Turnoff;
int t;cin>>t;
while(t--){
int n,k;cin>>n>>k;
for(int i=1;i<=n;i++)edge[i]=0,mp[i].clear();
for(int i=1;i<n;i++){
int u,v;cin>>u>>v;
mp[u].push_back(v);
mp[v].push_back(u);
edge[u]++;edge[v]++;
}
if(k==1)cout<<n-1<<endl;
else{
queue<int>leave;int ans=0;
for(int i=1;i<=n;i++){
if(edge[i]==1)leave.push(i);
vis[i]=0,viscnt[i]=0;
}
///不需要担心每次queue中弹出的点都要标记被访问
///之后会不会影响统计结果,因为被弹出的叶子节点
///已经被统计到它相邻的非叶子结点上从queue中删去不影响
///只用看相邻的非叶子结点是否满足viscnt[i]%k==0
while(!leave.empty()){
int u=leave.front();
leave.pop();vis[u]=1;
for(auto i:mp[u]){
if(vis[i])continue;
viscnt[i]++;edge[i]--;
if(viscnt[i]%k==0){///这里用%k==0而不用viscnt[i]==k
ans++; ///因为==k次操作一次 ==n*k操作n次
if(edge[i]==1)leave.push(i);
}
}
}
cout<<ans<<endl;
}
}
}