CF1554E You

statement

给你一个 (n) 个点的树,可以通过以下方式生成一个长度为 (n) 的序列 (a)

  • 每次在树中选取一个未被标记的节点 (u),令 (a_u) 等于与节点 (u) 相邻的未被标记的节点个数,然后将节点 (u) 标记

对于每一个整数 (kin[1,n]),输出符合以下条件的序列 (a) 的数量模 (998244353) 的值:

  • 序列 (a) 可以由给定的树通过上述方式生成;
  • (gcd(a_1,a_2,cdots,a_n)=k)

Hints

(2≤n≤10^5)

solution

看到这种 (gcd = k) ,直接设 (f(k)) 表示答案,然后发现 (f(1)) 不好算,设 (g(k)) 表示序列 (gcd)(k) 的倍数的总方案数。

有转移:

[f(k) = g(k) - sum_{kcdot s le n} f(kcdot s) ]

然后问题就变成求出一个 (g) 了。

发现按每个点不好算,因为会影响到与这个点链接的所有点,将对象改变成每条边。

其实影响序列 (a) 的只是由边相连的两个点,所以每条边会使得一条边所链接的其中一个点对应的 (a_i) 加一。

所以 (g(k)) 就是求出共有多少个合法序列中,每个元素都是 (k) 的倍数。

非常容易证明,对于两个序列 (a) ,若存在至少一条边所产生贡献的点是不一样的,且两个序列是相同的,那么与原来的图中必定有环存在。

所以 (g(1) = 2 ^ {n - 1})

至此,和原来树的形态没有任何关系。

考虑 (g(k) (k ge 2)) 的情况,可以发现,在原来的树中,度数比 (k) 小的必定使得它的 (a) 变为 (0)

归纳的想 (k = 2) 的情况,发现只有叶子节点度数小于 (2)

再从子树到树归纳的想,若一个叶子节点是 (k) 的倍数,那么这个点与父亲连的这一条边一定会让父亲的 (a) 加一,否则自己加一,如果做不到,那么 (g(k) = 0)

这样也说明了当 (k ge 2) 时,(g(k) = 0)(1)

但是暴力算是 (mathcal O (n ^ 2)) 的,这样的东西一般都是根号分治,但是这道题的式子是枚举倍数的,存在重复地把一些 (f) 加上再减去,可以只枚举质数倍数的 (f) ,但这是无关紧要的。

发现 (g(k) = 0) 的时候必须满足 (kmid (n - 1)) ,所以只要算这样的所有的 (g(k)) 即可。

#include <bits/stdc++.h>
#define forn(i,s,t) for(register int i=(s); i<=(t); ++i)
#define form(i,s,t) for(register int i=(s); i>=(t); --i)
#define rep(i,s,t) for(register int i=(s); i<(t); ++i)
using namespace std;
const int N = 1e5 + 3, Mod = 998244353;
struct Mint {
	int res;
	Mint() {}
	Mint(int a) : res(a) {}
	inline friend Mint operator + (const Mint& A, const Mint& B) {
		return Mint((A.res + B.res >= Mod) ? (A.res + B.res - Mod) : (A.res + B.res));
	}
	inline friend Mint operator - (const Mint& A, const Mint& B) {return A + Mint(Mod - B.res);}
	inline friend Mint operator * (const Mint& A, const Mint& B) {return Mint(1ll * A.res * B.res %Mod);}
	inline friend Mint& operator += (Mint& A, const Mint& B) {return A = A + B;}
	inline friend Mint& operator -= (Mint& A, const Mint& B) {return A = A - B;}
	inline friend Mint& operator *= (Mint& A, const Mint& B) {return A = A * B;}
	inline friend Mint q_pow(Mint p, int k) {
		static Mint res; res = Mint(1);
		for(; k; k >>= 1, p *= p) (k & 1) && (res *= p, 0);
		return res;
	}
};
Mint f[N]; 
struct Lst {
	int dir, nxt;
	Lst() {}
	Lst(int _d, int _n) : dir(_d), nxt(_n) {}
} E[N << 1];
int G[N], cnt, n;
inline void Add(int u, int v) {E[++cnt] = Lst(v, G[u]), G[u] = cnt;}
int dfn[N], ord, fa[N];
void Pre_dfs(int u) {
	for(register int i=G[u], v; i; i=E[i].nxt) {
		v = E[i].dir;
		if(v == fa[u]) continue ;
		fa[v] = u, Pre_dfs(v);
	}
	dfn[++ord] = u;
}
int w[N];
inline bool g(int k) {
	forn(i,1,n) w[i] = 0;
	rep(i,1,n) {
		int u = dfn[i];
		if(w[u] % k) w[u] ++ ;
		else w[fa[u]] ++ ;
		if(w[u] % k) return 0;
	}
	return 1;
}
inline void init() {forn(i,1,n) G[i] = 0; ord = cnt = 0;}
inline void solve() {
	scanf("%d", &n);
	rep(i,1,n) {
		static int u, v;
		scanf("%d%d", &u, &v);
		Add(u, v), Add(v, u);
	}
	Pre_dfs(1);
	form(i,n - 1,1) {
		if((n - 1) % i) f[i] = Mint(0);
		else if(i == 1) f[i] = q_pow(Mint(2), n - 1);
		else f[i] = Mint(g(i));
		for(register int j = i + i; j <= n - 1; j += i)
			f[i] -= f[j];
	}
	f[n] = Mint(0);
	forn(i,1,n) printf("%d%c", f[i].res, " 
"[i == n]);
}
int T;
int main() {
	scanf("%d", &T);
	while(T--) init(), solve();
	return 0;
}
原文地址:https://www.cnblogs.com/Ax-Dea/p/15234189.html