zoukankan      html  css  js  c++  java
  • THUPC2018 好图计数

    [THUPC2018]好图计数 [* easy~hard]

    观察:我们一定是先搞出大小为 (n) 的不连通好图,然后转为连通好图,不难发现不连通图的补图一定是联通图。

    于是设 (f_i) 表示大小为 (i) 的联通的好图的数量,不难发现其和不连通的好图数是相同的。转移直接枚举联通好图转移即可。

    对每个 (i) 我们搞出一个背包的模型的 GF,此时有:

    [prod_{i<n} (frac{1}{1-x^i})^{f_i}[x^n] ]

    就是我们需要的 (f_n),边界为 (f_1=1),答案为 (2f_n)

    然后我们可以得到:

    [egin{aligned} &F(x)=-1+x+prod_{i}(frac{1}{1-x^i})^{f_i}-F(x) \&2F(x)-x+1=prod_{i}(frac{1}{1-x^i})^{f_i} \&=exp(sum_{i}f_icdot ln(frac{1}{1-x^i})) \&=exp(sum_i sum_k f_ifrac{x^{ik}}{k}) \&=exp(sum_kfrac{1}{k}sum_i f_ix^{ik}) \&=exp(sum_kfrac{F(x^k)}{k}) \&igg(exp(sum_kfrac{F(x^k)}{k})+xigg)-2F(x)-1=0 end{aligned}]

    考虑方程 (G(F(x))) 在模 (x^{n/2}) 意义下的解为 (F_0(x)),考虑拓展到 (F(x)),根据泰勒展开:

    [sum frac{G(F_0(x))^{(i)}(F(x)-F_0(x))^i}{i!}=0 ]

    [egin{aligned} &G(F_0(x))'(F(x)-F_0(x))+G(F_0(x))=0 \&F(x)=F_0(x)-frac{G(F_0(x))}{G(F_0(x))'} end{aligned}]

    然后神奇的结论告诉我们 (F(x^2)) 是已知量所以求导的时候可以被视为常数,最后的导数是:

    [exp(sum_k frac{1}{k}F_0(x^k))-2 ]

    • 注意 (e^{x+c}) 的导数是 (e^{x+c})

    于是得到:

    [egin{aligned} F(x)=F_0(x)-frac{exp(sum_{k}frac{1}{k}F_0(x^k))-2F_0(x)+x-1}{exp(sum_{k}frac{1}{k}F_0(x^k))-2} end{aligned}]

    最后就做完了,exp+牛迭,(5cdot 10^4) 5s+ 一点也不过分吧(

    哦,还要任意模数,这该如何跑赢 (n^2) TAT (写不动)

    听说可以直接 (mathcal O(n^2)) 过 TAT:

    (F(x)) 表示联通的生成函数,(G(x)) 表示答案的生成函数且定义 (G(x)[x^0]=1)(更一般的情况有 (g_1=f_1=1,g_n=f_ncdot 2),可以得到:

    [G(x)=prod (frac{1}{1-x})^{f_i} ]

    [ln G(x)=sum_k frac{F(x^k)}{k} ]

    (G(x)) 的好处在于优美简洁的形式帮助我们避免了对 (F(x)) 的边界的讨论。

    众所周知的是 (ln) 是可以递推的,设 (B(x)=ln G(x))

    [egin{aligned} &B(x)'=(ln G(x))' \& B(x)'=frac{G'(x)}{G(x)} \& B(x)'G(x)=G'(x) \& B_n=G_n-frac{1}{n}sum_{i=1}^{n-1} B_i imes i imes G_{n-i} end{aligned}]

    首先,我们知道了 (g_n),那么就可以知道 (f_n)

    其次,我们知道了 (g_n),那么就可以知道 (ln G_n)

    问题在于我们并不知道 (g_n)

    回顾并比对我们的方程有:

    [g_n-b=frac{1}{2}g_n+c o g_n=(c+b)2 ]

    于是我们解决了这个问题。同理 (f_n=c-b)(for n > 1)

    (Code:)

    #include<bits/stdc++.h>
    using namespace std ;
    #define Next( i, x ) for( register int i = head[x]; i; i = e[i].next )
    #define rep( i, s, t ) for( register int i = (s); i <= (t); ++ i )
    #define drep( i, s, t ) for( register int i = (t); i >= (s); -- i )
    #define re register
    #define mp make_pair
    #define pi pair<int, int>
    #define pb push_back
    #define int long long
    #define vi vector<int>
    int gi() {
    	char cc = getchar() ; int cn = 0, flus = 1 ;
    	while( cc < '0' || cc > '9' ) {  if( cc == '-' ) flus = - flus ; cc = getchar() ; }
    	while( cc >= '0' && cc <= '9' )  cn = cn * 10 + cc - '0', cc = getchar() ;
    	return cn * flus ;
    }
    const int N = 3e4 + 5 ; 
    int lim, n, P, inv[N], f[N], g[N], B[N], c[N] ;  
    int fpow(int x, int k) {
    	int ans = 1, base = x ;
    	while(k) {
    		if(k & 1) ans = 1ll * ans * base % P ;
    		base = 1ll * base * base % P, k >>= 1 ;
    	} return ans ;
    }
    signed main()
    {
    	int T = gi() ; P = gi() ; 
    	lim = 23333 ; 
    	rep( i, 1, lim ) inv[i] = fpow( i, P - 2 ) ;
    	g[0] = 1, f[0] = 0, B[0] = 0 ; 
    	g[1] = 1, f[1] = 1, B[1] = 1 ; 
    	rep( i, 1, lim ) c[i] = inv[i] ; 
    	rep( i, 2, lim ) {
    		__int128 rb = 0, rt = 0 ; 
    		for(re int j = 1; j < i; ++ j) 
    		rt = B[j] * g[i - j], rt *= j, rb += rt ;
    		long long b = rb % P ; 
    		b %= P, b = b * inv[i] % P ;  
    		f[i] = (c[i] + b) % P, g[i] = f[i] * 2 % P ;
    		for(re int j = i; j <= lim; j += i)
    		c[j] = (c[j] + f[i] * inv[j / i] % P) % P ; 
    		B[i] = (g[i] - b + P) % P ; 
    	}
    	while( T-- ) {
    		int x = gi() ; 
    		cout << g[x] << endl ; 
    	}
    	return 0 ;
    }
    
  • 相关阅读:
    boost库:函数对象
    boost库:智能指针
    linux 查看和修改文件时间
    linux正则表达式
    UVA
    UVA
    UVA
    UVA
    UVA
    对JavaScript的认识?
  • 原文地址:https://www.cnblogs.com/Soulist/p/14013598.html
Copyright © 2011-2022 走看看