1. 程式人生 > >【bzoj4401】塊的計數 結論題

【bzoj4401】塊的計數 結論題

編號 時間復雜度 sdi 約數 blog ros lin bsp style

題目描述

給出一棵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;
}

【bzoj4401】塊的計數 結論題