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\)

然后我们可以得到:

\[\begin{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_i\cdot \ln(\frac{1}{1-x^i})) \\&=\exp(\sum_i \sum_k f_i\frac{x^{ik}}{k}) \\&=\exp(\sum_k\frac{1}{k}\sum_i f_ix^{ik}) \\&=\exp(\sum_k\frac{F(x^k)}{k}) \\&\bigg(\exp(\sum_k\frac{F(x^k)}{k})+x\bigg)-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 \]

\[\begin{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}\)

于是得到:

\[\begin{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+牛迭,\(5\cdot 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_n\cdot 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)\)

\[\begin{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\times i\times 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\to 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 ;
}
posted @ 2020-11-20 23:58  Soulist  阅读(124)  评论(0)    收藏  举报