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
", dp[m]);
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/19992147orz/p/8047136.html