zoukankan      html  css  js  c++  java
  • 题解 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);
    	}
    }
    
  • 相关阅读:
    linux 常用命令(个人记录)
    jmeter 5.0版本更新说明(个人做个记录)
    Netdata---Linux系统性能实时监控平台部署记录
    MySQL Yum存储库 安装、升级、集群
    linux 各项配置汇总
    构建Maven项目自动下载jar包
    计算服务器的pv量算法
    性能计算公式
    jstack(查看线程)、jmap(查看内存)和jstat(性能分析)命令
    结构模式
  • 原文地址:https://www.cnblogs.com/whx1003/p/14695435.html
Copyright © 2011-2022 走看看