[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 ;
}