bzoj3456 城市規劃(分治NTT)

bzoj3456 城市規劃

原題地址http://www.lydsy.com/JudgeOnline/problem.php?id=3456

題意:
阿狸的國家有n個城市, 現在國家需要在某些城市對之間建立一些貿易路線, 使得整個國家的任意兩個城市都直接或間接的連通. 爲了省錢, 每兩個城市之間最多只能有一條直接的貿易路徑. 對於兩個建立路線的方案, 如果存在一個城市對, 在兩個方案中是否建立路線不一樣, 那麼這兩個方案就是不同的, 否則就是相同的. 現在你需要求出一共有多少不同的方案。

換句話說, 你需要求出n個點的簡單(無重邊無自環)無向連通圖數目。

由於這個數字可能非常大, 你只需要輸出方案數mod 1004535809(479 * 2 ^ 21 + 1)即可。

數據範圍
n <= 130000

題解:
gii 個點的圖的數量,則gi=2n(n1)2 ,令fii 個點的簡單無向連通圖數目,有:

fi=gij=1i1fj(i1j1)gij

對於減掉的那個式子:
j=1i1fj(i1j1)gij=j=1i1(i1)!(j1)!(ij)!fjgij=(i1)!j=1i1fj(j1)!gij(ij)!
若令Fi=fi(i1)!Gi=gij(ij)!
fi=gi(i1)!j=1i1FjGij ,如果讓未算出的Fn=F0=0 ,且G0=0 ,減掉的就完全轉化爲了標準卷積形式,可以使用NTT了:
fi=gi(i1)!j=0iFjGij
由於計算出fi 需要用到f0...fi1 ,所以用分治NTT即可。

說起來好像很簡單的樣子,然而調題調一天…

第一個錯誤:wi沒有賦初值爲1。
第二個錯誤:預處理g數組時因爲指數是LL乘爆了。發現這個問題是因爲 44824這組數據過不去。

要注意的地方大概是分治時計算[lf,mid]的f對[mid+1,rg]的貢獻時,要想清楚哪一位具體對應哪一位。因爲lf需要加上(rg-lf)纔等於rg,所以用於相乘的g數組是[1,rg-lf]。

代碼:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define LL long long
using namespace std;
const int mod=1004535809;//479*2^21+1 g=3
const int N=600050;
int G=3;
int n,fac[N],inv[N],f[N],a[N],g[N],b[N],R[N],iv[N],w[N],_w[N];
int modpow(int A,LL B)
{
    int ret=1; int base=A;
    for(;B;B>>=1)
    {
        if(B&1) ret=(1LL*ret*base)%mod;
        base=(1LL*base*base)%mod;
    }
    return ret;
}
void NTT(int *x,int flag,int len)
{
    for(int i=0;i<len;i++) if(i<R[i]) swap(x[i],x[R[i]]);
    for(int m=2;m<=len;m<<=1)
    {
        int l=m>>1; int wn,wi=1;    
        if(flag==1) wn=w[m]; else wn=_w[m];
        for(int j=0;j<len;j+=m)
        {
            wi=1;
            for(int i=0;i<l;i++)
            {
                int y=(1LL*wi*x[i+j+l])%mod;
                x[i+j+l]=(x[i+j]-y+mod)%mod;
                x[i+j]=(x[i+j]+y)%mod;
                wi=(1LL*wi*wn)%mod;
            }
        }

    }
    if(flag==-1) for(int i=0;i<len;i++) x[i]=(1LL*x[i]*iv[len])%mod;
}
void solve(int lf,int rg)
{
    if(lf==rg) {f[lf]=(g[lf]-(1LL*f[lf]*fac[lf-1])%mod+mod)%mod; return;}
    int mid=(lf+rg)>>1;
    solve(lf,mid);
    int len=1,p=0; for(;len<2*(rg-lf+1);len<<=1,p++);
    R[0]=0; for(int i=1;i<len;i++) R[i]=(R[i>>1]>>1)|((i&1)<<(p-1));
    for(int i=0;i<=len;i++) a[i]=b[i]=0;
    for(int i=lf;i<=mid;i++) a[i-lf+1]=(1LL*f[i]*inv[i-1])%mod;
    for(int i=1;i<=rg-lf;i++) b[i]=(1LL*g[i]*inv[i])%mod;
    NTT(a,1,len); NTT(b,1,len);
    for(int i=0;i<len;i++) a[i]=(1LL*a[i]*b[i])%mod;
    NTT(a,-1,len);
    for(int i=mid+1;i<=rg;i++) f[i]=(f[i]+a[i-lf+1])%mod;
    solve(mid+1,rg);
}
int main()
{
    scanf("%d",&n); 
    inv[0]=fac[0]=inv[1]=fac[1]=1;
    for(int i=2;i<=4*n;i++) fac[i]=1LL*fac[i-1]*i%mod,inv[i]=modpow(fac[i],mod-2);
    g[0]=0; for(int i=1;i<=4*n;i++) g[i]=modpow(2,(1LL*i*(i-1)/2LL));
    for(int i=1,p=0;i<=4*n;i<<=1,p++)
    {w[i]=modpow(G,(mod-1)/i),_w[i]=modpow(w[i],mod-2),iv[i]=modpow(i,mod-2);}
    solve(1,n);
    printf("%d\n",f[n]);
    return 0;
}
發佈了203 篇原創文章 · 獲贊 43 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章