题解 2021 年 ICPC 昆明赛区 E. Counting Binary Trees

题目链接

考虑记根节点标号恰为 (n) 的方案数为 (f(n)),要求的就是

[egin{aligned} F(n)&=sum_{i=1}^n f(i) end{aligned} ]

而题目的条件相当于 (f=fotimes f+p),其中 (p(n)=[exists k_imid n])。注意到 (f(1)=0),于是这确实是良定义的。

然后把前缀和拆一下,就有

[egin{aligned} F(n)=&P(n)+sum_{i=1}^n(fotimes f)(i)\ =&P(n)+sum_{i,jgeq 1, ijleq n}f(i)f(j)\ =&P(n)+sum_{i=1}^nf(i)F(n/i) end{aligned} ]

这是可以直接递推的。具体来说,可以类似杜教筛地预处理前 (mathcal O(n^{2/3})) 的点值,然后后面的递归下去算,平衡一下块大小就能过。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<unordered_map>
#include<algorithm>

typedef long long ll;
const ll mod = 998244353;
const int maxn = 1E+6 + 5;

int T, n, m, N, k[10];
ll F[maxn];

inline bool p(int n) {
	for(int i = 0; i < m; ++i)
		if(n % k[i] == 0) return true;
	return false;
}

inline int sgn(int x) { return x & 1 ? -1 : 1; }

int LCM[100];
inline int lcm(int x, int y) { return x * y / std::__gcd(x, y);}

inline int P(int n) {
	int res = 0;
	for(int S = 1; S < (1 << m); ++S) {
		int ppcnt = 0;
		res += sgn(__builtin_popcount(S) + 1) * (n / LCM[S]);
	} return res;
}

std::vector<int> d[maxn];
inline void PRE(int N) {
	for(int d = 1; d <= N; ++d)
		for(int k = 1; k * d <= N; ++k)
			::d[d * k].push_back(d);
}

inline void pre(int N) {
	for(int i = 2; i <= N; ++i) {
		F[i] = p(i);
		for(int d : ::d[i])
			(F[i] += F[d] * F[i / d]) %= mod;
	}
	
	for(int i = 2; i <= N; ++i) (F[i] += F[i - 1]) %= mod;
}

std::unordered_map<int, ll> M;
inline ll sF(int n) {
	if(n <= N) return F[n];
	if(M.count(n)) return M[n];
	
	ll res = P(n);
	for(int l = 2, r; l <= n; l = r + 1) {
		r = n / (n / l); if(r == n) break;
		(res += (sF(r) - sF(l - 1)) * sF(n / l)) %= mod;
	}
	return M[n] = res;
}

int main() {
	scanf("%d", &T), PRE(6E+5);
	while(T --> 0) {
		scanf("%d%d", &n, &m), M.clear();
		
		for(int i = 0; i < m; ++i) scanf("%d", &k[i]);
		for(int S = 1; S < (1 << m); ++S) {
			LCM[S] = 1;
			for(int i = 0; i < m; ++i)
				if(S >> i & 1) LCM[S] = lcm(LCM[S], k[i]);
		}
		
		pre(N = pow(n, 0.666) / 2 + 50), printf("%lld
", (sF(n) + mod) % mod);
	}
}
原文地址:https://www.cnblogs.com/whx1003/p/14695435.html