zoukankan      html  css  js  c++  java
  • 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;
    } 
    
  • 相关阅读:
    Arch 真好用
    Spring 自定义注解-字段注解
    Raft论文概述
    Raft成员变化(Membership Change)
    Reactor模式详解
    高性能IO之Reactor模式
    WinFrm中多线程操作窗体属性
    Reactor模式
    高并发中的线程与线程池
    二层交换机与三层交换机区别详解!
  • 原文地址:https://www.cnblogs.com/Livingston/p/15558311.html
Copyright © 2011-2022 走看看