zl程序教程

您现在的位置是:首页 >  其它

当前栏目

【bzoj4401】块的计数 结论题

计数 结论
2023-09-11 14:22:39 时间

题目描述

给出一棵n个点的树,求有多少个si使得整棵树可以分为n/si个连通块。

输入

第一行一个正整数N,表示这棵树的结点总数,接下来N-1行,每行两个数字X,Y表示编号为X的结点与编号为Y的结点相连。结点编号的范围为1-N且编号两两不同。

输出

一行一个整数Ans,表示所求的方案数。

样例输入

6
1 2
2 3
2 4
4 5
5 6

样例输出

3


题解

结论题

结论:k可行当且仅当子树大小是k的倍数的节点数有n/k个。

这结论好像挺显然的(只要你能想出来。。。)

然后就做完了。。。先预处理出每个节点的size,对size维护桶,枚举n的约数,再枚举它的倍数求和统计即可。

时间复杂度为 $O(\sum\limits_{k|n}\frac nk=\sum\limits_{k|n}k=n\log\log n)$ 

#include <cstdio>
#include <cctype>
#define N 1000010
int head[N] , to[N << 1] , next[N << 1] , cnt , si[N] , v[N];
inline void add(int x , int y)
{
	to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt;
}
void dfs(int x , int fa)
{
	int i;
	si[x] = 1;
	for(i = head[x] ; i ; i = next[i])
		if(to[i] != fa)
			dfs(to[i] , x) , si[x] += si[to[i]];
	v[si[x]] ++ ;
}
inline char nc()
{
	static char buf[100000] , *p1 , *p2;
	return p1 == p2 && (p2 = (p1 = buf) + fread(buf , 1 , 100000 , stdin) , p1 == p2) ? EOF : *p1 ++ ;
}
inline int read()
{
	int ret = 0; char ch = nc();
	while(!isdigit(ch)) ch = nc();
	while(isdigit(ch)) ret = ((ret + (ret << 2)) << 1) + (ch ^ '0') , ch = nc();
	return ret;
}
int main()
{
	int n = read() , i , x , y , ans = 0;
	for(i = 1 ; i < n ; i ++ ) x = read() , y = read() , add(x , y) , add(y , x);
	dfs(1 , 0);
	for(i = 1 ; i <= n ; i ++ )
	{
		if(!(n % i))
		{
			for(x = 0 , y = i ; y <= n ; y += i) x += v[y];
			if(x == n / i) ans ++ ;
		}
	}
	printf("%d\n" , ans);
	return 0;
}