Loading

7385. 【2021.11.15NOIP提高组联考】你

Description

你是真的强,你是真的强,你是真的强。

有一颗很强的树,但是没你强,所以这棵树会枯萎。

这棵树枯萎的时候会产生一个强者序列 \(a\) , 具体的强者序列 \(a\) 生成方式如下:

  • 每次选择一个没有枯萎的点 \(u\), 然后 \(a_{u}\) 等于与 \(u\) 相邻的, 末枯萎的点数。

  • 然后 \(u\) 就会枯萎。

现在对于每一个 \(k \in[1, n]\) 你要求出合法的强者序列 \(a\) 的数量, 满足 \(g c d\left(a_{1}, a_{2}, a_{3}, a_{4}, \cdots\right)=k\) 答案对 \(998244353\) 取模。

\(n\le 10^6,\sum n\le 3\times 10^6\)

Solution

题目可以转换成对于每条边,指定一个方向,那么 \(a_i\) 就是节点 \(i\) 的入度。那么就有 \(\sum a_i=n-1\)

\(k>1\) 时,可以证明,答案为 0 或 1。

证明:由于 \(k>1\),那么叶子节点一定向他的父亲连边,也就是最底层是确定的,因此我们可以逐步向根走,那么整棵树都是确定的,答案非 0 即 1。

同时由于 \(\sum a_i=n-1\),那么 \(k\) 一定是 \(n-1\) 的约数。但是如果直接找约数时间复杂度是 \(\mathcal O(\sqrt{n})\) 的,再加上遍历的复杂度 \(\mathcal O(n)\),总复杂度 \(\mathcal O(n\sqrt{n})\),不足以通过此题。

思考一下,\(k\) 一定是 \(n-1\) 的某个质因子的倍数,因此如果我们令 \(k\) 为某个质因子,求出此情况下的 \(gcd(a_1,a_2,a_3,\cdots)\),则是真正的 \(k\),也只有这个 \(k\) 是可能有答案的。

\(k=1\) 的答案就是 \(2^{n-1}-sum\)\(sum\) 表示 \(k>1\) 时的答案总数。因为每条边只有两种情况,总情况是 \(2^{n-1}\),有 \(sum\) 种情况的 \(gcd\) 不为 1,那么剩下的就是 \(gcd=1\) 的情况。

Code

#include<cstdio>
#include<cstring>
#define N 1000005
#define mod 998244353
using namespace std;
struct node
{
	int to,next;
}a[N<<1];
int pt,T,n,tot,k,x,y,p[N],opt[N],ans[N],head[N],mi[N],c[N];
bool flag,b[N];
int read()
{
	int res=0,fh=1;char ch=getchar();
	while (ch<'0'||ch>'9') {if (ch=='-') fh=-1;ch=getchar();}
	while (ch>='0'&&ch<='9') res=(res<<1)+(res<<3)+(ch-'0'),ch=getchar();
	return res*fh;
}
void add(int x,int y)
{
	a[++tot].to=y;
	a[tot].next=head[x];
	head[x]=tot;
}
void dfs(int x,int fa)
{
	if (!flag) return;
	c[x]=0;
	for (int i=head[x];i;i=a[i].next)
	{
		int v=a[i].to;
		if (v==fa) continue;
		dfs(v,x);
		c[x]+=opt[v];
	}
	if (c[x]%k==0) opt[x]=1;
	else
	{
		if ((c[x]+1)%k!=0) flag=false;
		opt[x]=0;c[x]++;
	}
}
int gcd(int x,int y)
{
	if (!y||!x) return x+y;
	int r=x%y;
	while (r) x=y,y=r,r=x%y;
	return y;
}
int main()
{
	freopen("you.in","r",stdin);
	freopen("you.out","w",stdout);
	b[1]=true;mi[1]=2;
	for (int i=2;i<=1000000;++i)
	{
		mi[i]=mi[i-1]*2%mod;
		if (!b[i]) p[++pt]=i;
		for (int j=1;j<=pt;++j)
		{
			int k=i*p[j];
			if (k>1000000) break;
			b[k]=true;
			if (i%p[j]==0) break;
		}
	}
	T=read();
	while (T--)
	{
		tot=0;
		n=read();
		for (int i=1;i<=n;++i)
			head[i]=ans[i]=0;
		for(int i=1;i<n;++i)
		{
			x=read();y=read();
			add(x,y);add(y,x);			
		}
		ans[1]=mi[n-1];
		for (int i=1;i<=pt;++i)
		{
			if (p[i]>n-1) break;
			if ((n-1)%p[i]==0)
			{
				k=p[i];
				flag=true;
				dfs(1,0);
				if (flag)
				{
					int g=0;
					for (int j=1;j<=n;++j)
						g=gcd(g,c[j]);
					if (g<n&&!ans[g]) ans[g]=1,ans[1]--;
				}
			}
		}
		for (int i=1;i<=n;++i)
			printf("%d ",ans[i]);
		printf("\n");
	}
	return 0;
} 
posted @ 2021-11-15 21:19  Thunder_S  阅读(32)  评论(0编辑  收藏  举报