题意
给定一棵树,现在定义一次操作为"选定拥有同样父亲的 (k) 个叶节点,并将这 (k) 个叶节点一起删去。问最多能够进行多少次操作。
范围&性质: (1le kle nle 2 imes 10^5)
分析:
由于这是一颗无根树,所以我们无法求出DFS每个点的儿子和父亲
那么我们换个角度考虑,每次被删除的点有什么特点,我们发现它们的度数都是1
也就是说我们按照拓扑排序的方法将度数为1的点删掉,同时一个点能成为叶子的前提条件有两个:
- 它的儿子都能删掉(直接或间接成为叶子)
- 它的儿子的个数为 (k) 的倍数(否则它的儿子删不完全)
对于根节点要特殊处理,因为别的点都有一个父亲,但根节点没有,所以最后统计答案时要将根节点的度数 (+1)
代码:
#include<bits/stdc++.h>
using namespace std;
namespace zzc
{
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
const int maxn = 2e5+5;
int t,n,k,ans,cnt=0,idx,st;
int head[maxn],deg[maxn],val[maxn];
bool vis[maxn];
struct edge
{
int to,nxt;
}e[maxn<<1];
queue<int> q;
void add(int u,int v)
{
e[++cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt;
deg[v]++;
}
void toposort()
{
while(!q.empty()) q.pop();
for(int i=1;i<=n;i++) if(deg[i]==1) q.push(i);
while(!q.empty())
{
int u=q.front();q.pop();
if(vis[u]) continue;
vis[u]=true;
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].to;
if(!vis[v])
{
val[v]++;
if(val[v]%k==0&&val[v]==deg[v]-1) q.push(v);
}
}
}
for(int i=1;i<=n;i++) ans+=val[i]/k;
}
void work()
{
int a,b;
t=read();
while(t--)
{
cnt=0;ans=0;idx=0;
for(int i=1;i<=n;i++) head[i]=deg[i]=val[i]=0,vis[i]=false;
n=read();k=read();
for(int i=1;i<n;i++)
{
a=read();b=read();
add(a,b);add(b,a);
}
if(k==1)
{
printf("%d
",n-1);
continue;
}
toposort();
printf("%d
",ans);
}
}
}
int main()
{
zzc::work();
return 0;
}