• 【CF1554E】You


    题目

    题目链接:https://codeforces.com/contest/1554/problem/E
    给定一棵 (n) 个点的树,你需要依次删去 (n) 个点,当点 (i) 被删除时,(a_i) 会被赋值为目前与 (i) 相连且没有被删除的点的数量。
    对于 (k=1sim n),分别求最后 (gcd(a_1,a_2,cdots,a_n)=k) 的方案数。两种方案不同当且仅当存在一个点被删的时间不同。多测。
    (tleq 10^4,sum nleq 3 imes 10^5)

    思路

    删点不好搞,时间倒流就变成了对于每一条边,可以把 (1) 的权值加到连接的两个点的其中一个,问最后所有点权值的 (gcd=k) 的方案数。
    那么显然总方案数为 (2^{n-1}) 次。而且要求 (gcd) 恰好等于 (k),容易想到求出 (gcd)(k) 的倍数的方案数然后容斥一下。
    (f_i) 表示最后所有点 (gcd)(i) 的倍数的方案数。首先显然有 (f_1=2^{n-1})
    对于 (i>1),考虑如何构造出一个 (gcd)(i) 的倍数的解。
    首先对于叶子节点,他们的权值一定是 (0)。对于一个非叶子节点 (x),假设我们已经递归求出了它的所有儿子的权值,那么 (x) 与它儿子之间的边的权值加到哪一边就已经确定了。能影响 (x) 权值的只剩 (x) 与父亲的边。
    如果此时 (x) 的权值 (v)(k) 的倍数,那么 (v+1) 一定不是 (k) 的倍数,那么这条边的权值就给 (x) 的父亲;否则如果 (v+1)(k) 的倍数,权值就给 (x);否则无解。
    通过这样构造我们发现对于 (k>1) 的部分,(f_k) 只可能是 (0)(1)。所以对 (k=2sim n) 都 dfs 一遍就可以做到单次 (O(n^2)) 了。
    但是其中很多都是没有必要跑 dfs 的。观察到所有点的权值之和为 (n-1),那么所有点的 (gcd) 也一定是 (n-1) 的因子。只需要把 (n-1) 的因子拿出来跑 dfs 即可。
    时间复杂度 (O(Qn(log n+sigma_0(n))))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=100010,MOD=998244353;
    int Q,n,tot,head[N],f[N];
    vector<int> d[N];
    
    struct edge
    {
    	int next,to;
    }e[N*2];
    
    void add(int from,int to)
    {
    	e[++tot]=(edge){head[from],to};
    	head[from]=tot;
    }
    
    int dfs(int x,int fa,int k)
    {
    	int cnt=0;
    	for (int i=head[x];~i;i=e[i].next)
    	{
    		int v=e[i].to;
    		if (v!=fa)
    		{
    			int val=dfs(v,x,k);
    			if (val==-1) return -1;
    			cnt+=val;
    		}
    	}
    	if (cnt%k==0) return 1;
    	if (cnt%k==k-1) return 0;
    	return -1;
    }
    
    int main()
    {
    	for (int i=2;i<N;i++)
    		for (int j=i;j<N;j+=i) d[j].push_back(i);
    	scanf("%d",&Q);
    	while (Q--)
    	{
    		memset(head,-1,sizeof(head));
    		memset(f,0,sizeof(f));
    		tot=0; f[1]=1;
    		scanf("%d",&n);
    		for (int i=1,x,y;i<n;i++)
    		{
    			scanf("%d%d",&x,&y);
    			add(x,y); add(y,x);
    			f[1]=f[1]*2%MOD;
    		}
    		for (int i=0;i<d[n-1].size();i++)
    			if (dfs(1,0,d[n-1][i])==1) f[d[n-1][i]]=1;
    		for (int i=n;i>=1;i--)
    			for (int j=i*2;j<=n;j+=i) f[i]-=f[j];
    		for (int i=1;i<=n;i++) cout<<f[i]<<" ";
    		cout<<"
    ";
    	}
    	return 0;
    }
    
  • 相关阅读:
    移动前端开发之viewport的深入理解
    javascript的事件监听与捕获和冒泡
    AngularJS服务中serivce,factory,provider的区别
    使用shadow dom封装web组件
    Web Components之Custom Elements
    javascript 布尔类型值判断
    requestAnimationFrame()
    二十周年感言
    文件上传实例
    jhipster技术栈研究
  • 原文地址:https://www.cnblogs.com/stoorz/p/15078662.html
Copyright © 2020-2023  润新知