BZOJ-3456 (CDQ+NTT)

BZOJ-3456 (CDQ+NTT)

题意:求\(n\)个有标号点联通图的方案数

考虑减去\(n\)个点不连通的方案数

对于当前的\(i\)个点枚举1号点所在连通块大小为\(j(1,则方案数为\(C(i-1,j-1)\cdot dp_j\cdot 2^{(i-j)(i-j-1)/2}\)

即选出剩下的\(j-1\)个点,其他点随意

得到\(dp_i=2^{i(i-1)/2}-C(i-1,j-1)\cdot dp_j\cdot 2^{(i-j)(i-j-1)/2}\)

可以看到是一个与作差有关的转移,可以用\(CDQ+NTT\)优化

#include
using namespace std;
 
#define reg register
#define pb push_back
typedef long long ll;
#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)
 
template  inline void cmin(T &a,T b){ ((a>b)&&(a=b)); }
template  inline void cmax(T &a,T b){ ((a>=1,x=x*x%P) if(k&1) res=res*x%P;
    return res;
}
 
int rev[N];
ll A[N],B[N],w[N],g[N];
void NTT(int n,ll *a,int f) {
    rep(i,0,n-1) if(i>rev[i]) swap(a[i],a[rev[i]]);
    for(reg int i=1;i=P&&(a[j]-=P));
            }
        }
    }
    if(f==-1) {
        ll base=qpow(n,P-2);
        rep(i,0,n-1) a[i]=a[i]*base%P;
    }
}
 
 
void Solve(int l,int r) {
    if(l==r) return;
    if(r-l+1<=20) {
        rep(i,l,r) rep(j,i+1,r) dp[j]=(dp[j]-dp[i]*Fac[j-1]%P*Inv[i-1]%P*Inv[j-i]%P*g[j-i]%P+P)%P;
        return;
    } // 底层展开优化
    int mid=(l+r)>>1;
    Solve(l,mid);
    int R=1,cc=-1;
    while(R<=r-l+1) R<<=1,cc++;
    rep(i,1,R) rev[i]=(rev[i>>1]>>1)|((i&1)<

你可能感兴趣的:(BZOJ-3456 (CDQ+NTT))