THUPC2018 好图计数
[THUPC2018]好图计数 [* easy~hard]
观察:我们一定是先搞出大小为 \(n\) 的不连通好图,然后转为连通好图,不难发现不连通图的补图一定是联通图。
于是设 \(f_i\) 表示大小为 \(i\) 的联通的好图的数量,不难发现其和不连通的好图数是相同的。转移直接枚举联通好图转移即可。
对每个 \(i\) 我们搞出一个背包的模型的 GF,此时有:
就是我们需要的 \(f_n\),边界为 \(f_1=1\),答案为 \(2f_n\)
然后我们可以得到:
考虑方程 \(G(F(x))\) 在模 \(x^{n/2}\) 意义下的解为 \(F_0(x)\),考虑拓展到 \(F(x)\),根据泰勒展开:
然后神奇的结论告诉我们 \(F(x^2)\) 是已知量所以求导的时候可以被视为常数,最后的导数是:
- 注意 \(e^{x+c}\) 的导数是 \(e^{x+c}\)
于是得到:
最后就做完了,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)\) 的好处在于优美简洁的形式帮助我们避免了对 \(F(x)\) 的边界的讨论。
众所周知的是 \(\ln\) 是可以递推的,设 \(B(x)=\ln G(x)\):
首先,我们知道了 \(g_n\),那么就可以知道 \(f_n\)
其次,我们知道了 \(g_n\),那么就可以知道 \(\ln G_n\)
问题在于我们并不知道 \(g_n\)
回顾并比对我们的方程有:
于是我们解决了这个问题。同理 \(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 ;
}

浙公网安备 33010602011771号