P2351 [SDOI2012]吊灯

洛谷 p2351 [SDOI2012]吊灯

题目分析

题目链接

看完这个题后,我们可以得出一个很显然的结论,要保证一颗节点数为n的树可以分成若干个大小

为i的连通块,一定有i | n且定有n/i个节点的子树节点数之和(包括其本身)是i的倍数,(很显然,但
不会证
)

所以思路就很简单了,统计每个点的子节点个数,然后进行判断就行了

最开始统计个数用了dfs(本题这么明显的优化提示没注意) 期望得分70,但统计倍数个数的时候时

间复杂度炸了,被卡到40。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
using namespace std;
const int maxn=2e6+10;
inline int read(){
	int ret=0;
	int f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')
			f=-f;
		ch=getchar();
	}
	while(ch<='9'&&ch>='0'){
		ret=ret*10+(ch^'0');
		ch=getchar();
	}
	return f*ret;
}
int num[maxn];
int n;
int size[maxn];
int f[maxn];
int main(){
	freopen("a.in","r",stdin);
	n=read();
	int t=10;
	while(t--){
		for(int i=2;i<=n;i++){
			if(t==9){
				f[i]=read();
			}
			else{
				f[i]=(f[i]+19940105)%(i-1)+1;
			}	
		}
		memset(num,0,sizeof(num));
		for(int i=1;i<=n;i++){
			size[i]=1;
		}
		for(int i=n;i>1;i--){
		//	cout<<f[i]<<endl;
			size[f[i]]+=size[i];
		} 
		for(int i=1;i<=n;i++){
		//	cout<<size[i]<<endl;
			num[size[i]]++;	
		}
		cout<<"Case #"<<10-t<<':'<<endl;
		for(int l=1,r;l<=n;l=r+1){
			r=n/(n/l);//整除优化,能稍微快一点,把此处的n优化为根号n
			int t=0;
			for(int j=1;r*j<=n;++j) {
                t+=num[r*j];
			}
			if(t*r==n) cout<<r<<endl;
		}
	}
	return 0;
}

完结撒花!

posted @ 2020-10-12 11:51  折翼的小鸟先生  阅读(59)  评论(0编辑  收藏  举报