洛谷P3915 树的分解 题解
题目链接:P3915 树的分解
题意:
给出 \(N\) 个点的树和 \(K\),问能否把树划分成 \(\frac{N}{K}\) 个连通块,且每个连通块的点数都是 \(K\)。
输入格式:
第一行,一个整数 \(T\),表示数据组数。接下来 \(T\) 组数据,对于每组数据:
第一行,两个整数 \(N, K\)。
接下来 \(N - 1\) 行,每行两个整数 \(A_i, B_i\),表示边 \((A_i, B_i)\)。点用 \(1, 2, \ldots, N\) 编号。
输出格式:
对于每组数据,输出
YES
或NO
。数据范围:
对于 \(100 \%\) 的数据,\(1 \le T \le 10\),\(1 \le N ,K \le 10^5\)。
贪心好题。
首先 \(n \bmod k \ne 0\) 肯定无解。
考虑从下往上依次考虑每个点
如果它的 \(\mathtt{sz}_u = k\) 那就把它的砍掉,并令 \(\mathtt{sz}_u \gets 0\)
在有解的情况下,这么做一定可以分出来。
如果某个 \(\mathtt{sz}_u > k\) 怎么办呢? 可以发现此时一定无解,根据归纳法可证。
因为此时如果强行切出一个 \(k\) 的块,则 \(u\) 至少有一个儿子 \(v\) 无法被切割。
时间复杂度 \(\mathcal{O}(Qn)\)
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
namespace FastIO
{
#define gc() readchar()
#define pc(a) putchar(a)
#define SIZ (int)(1e6+15)
char buf1[SIZ],*p1,*p2;
char readchar()
{
if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);
return p1==p2?EOF:*p1++;
}
template<typename T>void read(T &k)
{
char ch=gc();T x=0,f=1;
while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
k=x*f;
}
template<typename T>void write(T k)
{
if(k<0){k=-k;pc('-');}
static T stk[66];T top=0;
do{stk[top++]=k%10,k/=10;}while(k);
while(top){pc(stk[--top]+'0');}
}
}using namespace FastIO;
#define N ((int)(1e5+15))
int n,k,ans,sz[N];
vector<int> g[N];
void dfs(int u,int f)
{
sz[u] = 1;
for(int v : g[u])
{
if(v == f) continue;
dfs(v,u); sz[u] += sz[v];
}
if(sz[u] == k) ++ans, sz[u] = 0;
}
void solve()
{
read(n); read(k); ans = 0;
for(int i=1; i<=n; i++) { sz[i] = 0; g[i].clear(); }
for(int i=1,u,v; i<n; i++)
{
read(u); read(v);
g[u].push_back(v); g[v].push_back(u);
}
if(n % k != 0) return puts("NO"),void(0);
dfs(1,1); puts(((ans == n/k) ? ("YES") : ("NO")));
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
int Q; read(Q); while(Q--) solve();
return 0;
}