bzoj3456
分治+ntt
设dp[i]表示i个点的图联通的方案数
那么考虑dp,利用容斥,总-不符合,枚举j=1->i-1,然后考虑不符合,那么考虑和1联通的连通块,剩下的不和1连通,那么dp[i]=2^t(i)-∑j=1->i-1 dp[j]*C(i-1,j-1)*2^t(i-j)
就是t(i)表示i个点的图的边的个数,那么相当于在j个点的连通图又添加了i-j个点,计算从i-1选出j-1个方案数,这是组合数,然后剩下的不能喝这j个点连,那么自己内部随便连,就是这个式子
但是这是n^2的,我们化简式子变成卷积,dp[j]*C(i-1,j-1)*2^t(i-j)
dp[j]*(i-1)!/(j-1)!/(i-j)!*2^t(i-j)
(dp[j]/(j-1)!)*2^t(i-j)/(i-j)
这是卷积,但是由于dp[i]和dp[j]有关,那么用cdq优化。
这是第二次写,还是搞不太清楚下标问题,其实就是构造多项式的时候每个式子个往前移了多少,那么最后统计的时候也往后移动多少
完全不卡常啊
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 260005, P = 1004535809; int n, m, k; ll h[N], a[N], b[N], inv[N], facinv[N], fac[N], dp[N], t[N]; ll power(ll x, ll t) { ll ret = 1; for(; t; t >>= 1, x = x * x % P) if(t & 1) ret = ret * x % P; return ret; } void ntt(ll *a, int f) { for(int i = 0; i < n; ++i) { int t = 0; for(int j = 0; j < k; ++j) if(i >> j & 1) t |= 1 << (k - j - 1); if(i < t) swap(a[i], a[t]); } for(int l = 2; l <= n; l <<= 1) { ll w = power(3, f == 1 ? (P - 1) / l : (P - 1) - (P - 1) / l); int m = l >> 1; for(int i = 0; i < n; i += l) { ll t = 1; for(int k = 0; k < m; ++k, t = t * w % P) { ll x = a[i + k], y = a[i + k + m] * t % P; a[i + k] = (x + y) % P; a[i + k + m] = ((x - y) % P + P) % P; } } } if(f == -1) { ll inv = power(n, P - 2); for(int i = 0; i < n; ++i) a[i] = a[i] * inv % P; } } void cdq(int l, int r) { if(l == r) return; int mid = (l + r) >> 1; cdq(l, mid); n = 1; k = 0; while(n <= r - l + 1) n <<= 1, ++k; for(int i = 0; i < n; ++i) a[i] = b[i] = 0; for(int i = l; i <= mid; ++i) a[i - l] = dp[i] * facinv[i - 1] % P; for(int i = 1; i <= r - l; ++i) b[i] = h[i] * facinv[i] % P; ntt(a, 1); ntt(b, 1); for(int i = 0; i < n; ++i) a[i] = a[i] * b[i] % P; ntt(a, -1); for(int i = mid + 1; i <= r; ++i) dp[i] = ((dp[i] - a[i - l] * fac[i - 1] % P) % P + P) % P; cdq(mid + 1, r); } int main() { scanf("%d", &m); fac[0] = facinv[0] = 1; inv[0] = inv[1] = 1; for(int i = 1; i <= m; ++i) { if(i != 1) inv[i] = (P - P / i) * inv[P % i] % P; fac[i] = fac[i - 1] * i % P; facinv[i] = facinv[i - 1] * inv[i] % P; t[i] = (ll)i * (i - 1) >> 1; dp[i] = h[i] = power(2, t[i]); } cdq(1, m); printf("%lld\n", dp[m]); return 0; }