• 【THUPC2018】好图计数


    题意

    我们归纳定义一个无向简单图是好的

    1. 一个单点是好的。
    2. 若干个好的图分别作为联通块所形成的图是好的。
    3. 一个好的图的补图是好的。

    给定一个正整数 (n)

    (n) 个点的本质不同的好的图的数量对质数 (P) 取模的结果。

    两个好的图的被认为是本质不同的,当且仅当无论如何将一个图重标号,它都不能与另一个图完全相同。

    数据范围: $ T leq 233, n leq 23333$

    题解

    先推导一些关于好图的性质:

    • 除了单个点之外,原图及其补图都是连通图的图不是好图。

    这个结论非常显然。

    • 一张图和它的补图不会同时为非连通图。

    考虑一张非连通图的点 ((u, v)), 如果原先不连通,则现在有边;如果原先连通,则存在一个点 (k) 在原图中和 (u, v) 都没有边,那么补图中边 ((u, k)) 和 $ (v, k)$ 就存在。

    这两个十分显然的结论就会推出一个极其有用的结论:

    • 除了单个点之外,连通好图和非连通好图成对出现。

    (f_n)(n) 个点好图个数, (n ot= 1) 时, (g_n) 为连通好图个数,也为非连通好图个数。

    连通好图可以划分为若干个非连通好图:

    [g_n = prod_{i = 1} ^ {n - 1} (sum_{j geq 0} inom{j + g_i - 1}{g_i - 1} x^{ij} ) [x^n] ]

    从而就可以得到 (f)(OGF)

    [egin{split} F &=prod_{igeq 1}(sum_{jgeq 0} inom{j + g_i - 1}{g_i - 1} x ^ {ij}) \ &= prod_{igeq 1} (1 - x^i) ^{ -g_i} end{split} ]

    两边取 (ln):

    [ln F = - sum_{i geq 1} g_i ln(1 - x^i) ]

    两边求导:

    [frac{F'}{F} = sum_{i geq 1} g_i frac{i x ^ {i - 1}}{1 - x^i} ]

    这就得到:

    [F' = F sum_{i geq 1} g_i frac{i x ^ {i - 1}}{1 - x^i} \(n+1)f_{n+1} = sum_{i = 0} ^ n f_{n-i} (sum_{j geq 1} g_j frac{j x ^ {j - 1}}{1 - x^j} [x^{i}]) ]

    因为有:

    [frac{x^{j - 1}}{1 - x ^ j} = sum_{i leq 1} x^{ij-1} ]

    因此(frac{x^{j - 1}}{1 - x ^ j} [x_i] = [i | (j + 1)]), 得到:

    [(n + 1) f_{n +1} = sum_{i = 0} ^ n f_i sum_{j | n + 1 - i} j g_j ]

    特判 $ i = 0$ 和 $ n = 1$,然后 (O(n^2)) 递推即可。

    用 __int128 优化掉取模之后跑得飞快。

    #pragma GCC optimize("2,Ofast,inline")
    #include<bits/stdc++.h>
    using namespace std;
    const int N = 23334;
    
    int n, m, mod;
    int f[N], g[N], h[N];
    
    const int Maxn = 23334;
    	
    int fac[Maxn], fav[Maxn], inv[Maxn];
    
    void comb_init() {
    	fac[0] = fav[0] = 1;
    	inv[1] = fac[1] = fav[1] = 1;
    	for (int i = 2; i < Maxn; ++i) {
    		fac[i] = 1LL * fac[i - 1] * i % mod;
    		inv[i] = 1LL * -mod / i * inv[mod % i] % mod + mod;
    		fav[i] = 1LL * fav[i - 1] * inv[i] % mod;
    	}
    }
    
    inline void upd(int &x, int y) {
    	(x += y) >= mod ? x -= mod : 0;
    }
    
    inline int add(int x, int y) {
    	return (x += y) >= mod ? x - mod : x;
    }
    
    inline int dec(int x, int y) {
    	return (x -= y) < 0 ? x + mod : x;
    }
    
    void doit(int x) {
    	int del = 1LL * f[x] * x % mod;
    	for (int i = x; i < N; i += x) {
    		upd(h[i], del);
    	}
    }
    
    void solve() {
    	g[0] = 1;
    	g[1] = f[1] = 1;
    	doit(1);
    	for (int i = 2; i < N; ++i) {
    		__int128 tmp = 0;
    		for (int j = 1; j < i; ++j) {
    			tmp += (long long) g[j] * h[i - j];
     		}
    		for (int j = 1; j * j <= i; ++j) {
    			if (i % j == 0) {
    				tmp += (long long) j * f[j];
    				if (j * j != i) {
    					tmp += (long long) (i / j) * f[i / j];
    				}
    			}
    		}
    		g[i] = tmp % mod;
    		g[i] = 2LL * g[i] * inv[i] % mod;
    		f[i] = 1LL * g[i] * inv[2] % mod;
    		doit(i);
    	}
    }
    
    int main() {
    	int T;
    	cin >> T >> mod;
    	comb_init();
    	solve();
    	while (T--) {
    		scanf("%d", &n);
    		printf("%d
    ", g[n] % mod);
    	}
    	return 0;
    }
    
  • 相关阅读:
    【CF 359B】Permutation
    如何更新 DevC++ 的编译器
    【LG 2801】教主的魔法
    矩阵浅谈
    NOI 系列赛常见技术问题整理
    Treap 浅谈
    DP 优化浅谈
    友链
    【CF 708C】Centroids
    我跳过的坑
  • 原文地址:https://www.cnblogs.com/Vexoben/p/11786119.html
Copyright © 2020-2023  润新知