zoukankan      html  css  js  c++  java
  • 题解 P4233 【射命丸文的笔记】 && 考试T3

    • 考虑每一条哈密顿回路在所有竞赛图中的出现次数。
    • 发现如果确定一个环, 其他的边乱选就可以保证出现哈密顿回路。所以对于一条哈密顿回路, 出现次数为(2^{C_n^2-n}), 减去的(n)为那(n)条边。哈密顿回路是(1-n)的一个排列首尾拼在一起, 共有(n!/n)种。于是总贡献可以直接得出。
    • 总贡献算好了, 问题转化为求有多少个存在哈密顿回路的竞赛图。
    • 显然, 存在哈密顿回路的竞赛图一定强连通, 强连通的竞赛图一定有哈密顿回路。
    • 所以问题就是求有多少个强连通的(n)个点的竞赛图。
    • (n)个点的强连通的竞赛图个数为(f_n)
    • 考虑用所有图减去不是强连通的竞赛图, 可以得到:

      [f_n =2^{C_n^2} - sum_{i=1}^{n - 1}f_i imes C_n^i imes 2^{C_{n - i}^2} ]

    • 这个式子的意思是枚举一个其他点无法到达的强连通图,也就是缩点之后拓扑序最小的强连通图,由于是任意的, 所以乘以组合数, 然后剩下的点数为(n - i)的图乱选。之所以可以这样是因为每个图一定会有一个最小的强连通图, 而且由于对于每个图我们只统计最小的, 所以做到不重不漏。
    • 考虑如何计算(f_i)
    • (g_i=2^{C_i^2}), 有:

    [f_n=g_n-sum_{i=1}^{n-1}f_i imes C_n^i imes g_{n-i} ]

    [g_n=sum_{i=1}^nf_i imes C_n^i imes g_{n-i} ]

    [frac{g_n}{n!}=sum_{i=1}^nfrac{f_i}{i!} imes frac{g_{n-i}}{(n-i)!} ]

    • (G_n=frac{g_n}{n!}), (F_n=frac{f_n}{n!})
    • 所以有:

      [G_n = sum_{i=1}^nF_iG_{n-i} ]

    • (A=sum_{i = 0}^{inf}F_ix^i), (B=sum_{i=0}^{inf}G_ix^i), 然后有:

    [A * B=sum_{i=0}^{inf}x^isum_{j+k=i}F_j imes G_k = B - G_0 ]

    • 这里(G_0 = 0)
    • 移项,多项式求逆就好了。
    #include <cmath>
    #include <cstdio>
    #include <vector>
    #include <cstring>
    #include <algorithm>
    #include <iostream>
    
    using namespace std;
    
    #define R register
    const int N = 4e5 + 10;
    const int P = 998244353;
    const int G = 3;
    
    inline int read() {
    	int x = 0, f = 1; char a = getchar();
    	for(; a > '9' || a < '0'; a = getchar()) if(a == '-') f = -1;
    	for(; a >= '0' && a <= '9'; a = getchar()) x = x * 10 + a - '0';
    	return x * f;
    }
    template <typename T>
    inline int qpow(int x, T k) {
    	int res = 1;
    	while(k) {
    		if(k & 1) res = 1LL * res * x % P;
    		x = 1LL * x * x % P; k >>= 1;
    	} return res;
    } 
    
    struct Poly {
    	int lim, rev[N];
    	inline void init(int Lim) {
    		lim = Lim;
    		for(R int i = 0; i < lim; i ++)
    			rev[i] = (rev[i >> 1] >> 1) | ((i & 1) ? lim >> 1 : 0);
    	}
    	inline void NTT(int *A, int type = 1) {
    		for(R int i = 0; i < lim; i ++) if(i > rev[i]) swap(A[i], A[rev[i]]);
    		for(R int dep = 1; dep < lim; dep <<= 1) {
    			int Wn = qpow(type == 1 ? G : qpow(G, P - 2), (P - 1) / (dep << 1));
    			for(R int j = 0, len = dep << 1; j < lim; j += len) {
    				int w = 1;
    				for(R int k = 0; k < dep; k ++, w = 1LL * w * Wn % P) {
    					int x = A[j + k], y = 1LL * w * A[j + k + dep] % P;
    					A[j + k] = (x + y) % P;
    					A[j + k + dep] = (x - y + P) % P;
    				}
    			}
    		}
    		if(type == -1) {
    			int inv = qpow(lim, P - 2);
    			for(R int i = 0; i < lim; i ++) A[i] = 1LL * A[i] * inv % P;
    		}
    	}
    	int C[N];
    	inline void getinv(int dep, int *F, int *G) {
    		if(dep == 1) {
    			G[0] = qpow(F[0], P - 2); return ;
    		}
    		getinv((dep + 1) >> 1, F, G);
    		int lim = 1;
    		while(lim < (dep << 1)) lim <<= 1; init(lim);
    		for(R int i = 0; i < dep; i ++) C[i] = F[i]; 
    		for(R int i = dep + 1; i < lim; i ++) C[i] = 0;
    		NTT(C); NTT(G);
    		for(R int i = 0; i < lim; i ++) 
    			G[i] = 1LL * (2 - 1LL * C[i] * G[i] % P + P) % P * G[i] % P;
    		NTT(G, -1);
    		for(R int i = dep; i < lim; i ++) G[i] = 0;
    	}
    }T;
    int n;
    int f[N], g[N], F[N];
    int fac[N], ifac[N];
    int main() {
    	freopen("hamilton.in", "r", stdin);
    	freopen("hamilton.out", "w", stdout);
    	n = read(); fac[0] = 1;
    	if(n >= 1) cout << 1 << endl;
    	if(n >= 2) cout << -1 << endl;
    	for(R int i = 1; i <= n; i ++) fac[i] = 1LL * fac[i - 1] * i % P;
    	ifac[n] = qpow(fac[n], P - 2);
    	for(R int i = n - 1; i >= 0; i --) ifac[i] = 1LL * ifac[i + 1] * (i + 1) % P;
    	for(R int i = 0; i <= n; i ++) g[i] = 1LL * qpow(2, 1LL * i * (i - 1) / 2) * ifac[i] % P;
    	T.getinv(n + 1, g, F);
    	for(R int i = 0; i <= n; i ++) F[i] = (P - F[i]) % P;
    	for(R int i = 0; i <= n; i ++) f[i] = 1LL * F[i] * fac[i] % P;
    	for(R int i = 0; i <= n; i ++) g[i] = 1LL * g[i] * fac[i] % P;
    	for(R int i = 3; i <= n; i ++) 
    printf("%lld
    ", 1LL * fac[i] * qpow(i, P - 2) % P * qpow(2, 1LL * i * (i - 1) / 2 - i) % P * qpow(f[i], P - 2) % P) ;
    	return 0;
    }
    
    
  • 相关阅读:
    lintcode:Palindrome Partitioning 分割回文串
    lintcode:Add Binary 二进制求和
    lintcode :Count 1 in Binary 二进制中有多少个1
    lintcode : 二叉树的最小深度
    lintcode :二叉树的最大深度
    lintcode:Binary Tree Postorder Traversal 二叉树的后序遍历
    lintcode :Binary Tree Preorder Traversal 二叉树的前序遍历
    lintcode:二叉树的中序遍历
    lintcode:Binary Search 二分查找
    lintcode:1-10题
  • 原文地址:https://www.cnblogs.com/clover4/p/13506016.html
Copyright © 2011-2022 走看看