【2017-2018 ACM-ICPC, Central Europe Regional Contest (CERC 17)】Justified Jungle【树上思维题】

题意:

给定一颗树,要求用 k k 刀将树切成等大小的 k + 1 k+1 块,问 k k 有哪些可能取值。将所有可能取值输出。


思路:

这个问题一开始看到有点懵,我们可以将其进行转化,先考虑比较简单的情况。

如果要求用 x x 刀把树切成等块,问你是否可能?这个问题就比较容易考虑,如果某一个节点的子树大小刚好为 n / ( x + 1 ) n/(x+1) ,则这个节点连向父节点的边是一定要切的,因此我们暴力切树即可。

然后可以发现,如果某个节点的子树大小为 n / ( k + 1 ) n/(k+1) 的倍数,那么这个节点连向父节点的边是一定要切的。因此我们统计子树大小为 n / ( k + 1 ) n/(k+1) 倍数的个数。如果这个个数恰好等于 k + 1 k+1 ,则说明可以用 k k 刀对树进行划分,否则不能。

因此我们用一个 v i s vis 来记录, v i s [ x ] vis[x] 表示有多少个节点的子树大小为 x x ,然后枚举每一个 k k ,进行判断即可。


代码:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define rep(i,a,b) for(int i = a; i <= b; i++)
#define LOG1(x1,x2) cout << x1 << ": " << x2 << endl;
#define LOG2(x1,x2,y1,y2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << endl;
typedef long long ll;
typedef double db;
const db EPS = 1e-9;
using namespace std;
const int N = 1e6+1000;

int tot,head[N],n,vis[N],siz[N];
struct Edge{
	int to,next;
}e[2*N];

void add(int x,int y){
	e[++tot].to = y, e[tot].next = head[x], head[x] = tot;
}

void dfs(int x,int pre){
	siz[x]++;
	for(int i = head[x]; i; i = e[i].next){
		int y = e[i].to;
		if(y == pre) continue;
		dfs(y,x);
		siz[x] += siz[y];
 	}
 	vis[siz[x]]++;
}

bool solve(int k)
{
	int cnt = 0;
	if(n % (k+1)) return 0;
	int s = n/(k+1);
	while(s <= n){
		if(vis[s]) cnt+=vis[s];
		s += n/(k+1);
	} 
	//LOG2("cnt",cnt,"k",k);
	return cnt == (k+1);
}

int main()
{
	tot = 0;
	scanf("%d",&n);
	rep(i,1,n-1){
		int x,y;
		scanf("%d%d",&x,&y);
		add(x,y), add(y,x);
	}
	dfs(1,0);
	rep(i,1,n){
		if(solve(i)) printf("%d ",i);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41552508/article/details/88722582