JZOJ 3303. 【集訓隊互測2013】城市規劃
題目
Description
剛剛解決完電力網絡的問題, 阿狸又被領導的任務給難住了.
剛纔說過, 阿狸的國家有n 個城市, 現在國家需要在某些城市對之間建立一些貿易路線, 使得整個國家的任意兩個城市都直接或間接的連通.
爲了省錢, 每兩個城市之間最多隻能有一條直接的貿易路徑. 對於兩個建立路線的方案, 如果存在一個城市對, 在兩個方案中是否建立路線不一樣, 那麼這兩個方案就是不同的, 否則就是相同的. 現在你需要求出一共有多少不同的方案.
好了, 這就是困擾阿狸的問題. 換句話說, 你需要求出n 個點的簡單(無重邊無自環)無向連通圖數目.
由於這個數字可能非常大, 你只需要輸出方案數mod 1004535809(479 * 2 ^21 + 1)即可.
Input
僅一行一個整數n(<=130000)
Output
僅一行一個整數, 爲方案數mod 1004535809.
Sample Input
輸入1:
3
輸入2:
4
輸入3:
100000
Sample Output
輸出1:
4
輸出2:
38
輸出3:
829847355
Data Constraint
對於20%的數據, n <= 10
對於40%的數據, n <= 1000
對於60%的數據, n <= 30000
對於80%的數據, n <= 60000
對於100%的數據, n <= 130000
題解
- 這種計數題,按照常規的思路,先考慮一下遞推,
- 設的答案爲,那麼可以由怎麼轉移,
- 經過思考後,發現好像不是很可行。
- (也許可以,至少多數人都沒有推出來)
- 那我們想想容斥(不要怕),用總方案數減去不可行的方案數(這就是容斥的全部),
- 開始推一波式子:
- 是個點之間所有無向邊選或不選的總方案數;
- 是枚舉任意一個點(比如說號點)所在的連通塊的大小;
- 表示該連通塊組成的方案數(當點已經確定的情況下);
- 是在剩餘點中選出與組成大小爲的連通塊的方案數;
- 是該連通塊以外的點之間所有邊選或不選的方案數;
- 這樣可以保證不重不漏!!!
- 設表示,也就是個點之間所有無向邊選或不選的總方案數,
- 右邊顯然是個卷積的形式,用來做,
- 但是要做次,那麼用分治.
代碼
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define md 1004535809
#define LL long long
#define N 300010
LL f[N],g[N],q[N],ny[N],ws[N],wt[N],nn[N];
LL a[N],b[N],rev[N];
LL ksm(LL x,LL y)
{
if(!y) return 1;
LL l=ksm(x,y/2);
if(y%2) return l*l%md*x%md;
return l*l%md;
}
void NTT(LL *a,int ln,int p)
{
for(int i=0;i<ln;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
for(int i=1;i<ln;i*=2)
{
LL w=ws[i*2];
if(p==-1) w=wt[i*2];
for(int j=0;j<ln;j+=2*i)
{
LL w0=1;
for(int k=0;k<i;k++)
{
LL A=a[k+j],B=w0*a[k+j+i];
a[k+j]=(A+B%md)%md;
a[k+j+i]=(A-B%md+md)%md;
w0=w0*w%md;
}
}
}
if(p==-1)
{
for(LL i=0;i<ln;i++) a[i]=a[i]*nn[ln]%md;
}
}
void cdq(LL l,LL r)
{
if(l==r)
{
f[l]=(g[l]-f[l]*q[l-1]%md+md)%md;
}
else
{
int mid=(l+r)/2,t=0,s=1,i;
cdq(l,mid);
for(;s<(r-l+1);s*=2) t++;
s*=2;
rev[0]=0;
for(i=1;i<s;i++) rev[i]=rev[i/2]/2|((i&1)<<t);
for(i=0;i<=mid-l;i++) a[i]=f[i+l]*ny[i+l-1]%md;
for(i=mid-l+1;i<s;i++) a[i]=0;
for(i=1;i<=r-l;i++) b[i]=g[i]*ny[i]%md;
b[0]=0;
for(i=r-l+1;i<s;i++) b[i]=0;
NTT(a,s,1),NTT(b,s,1);
for(i=0;i<s;i++) a[i]=a[i]*b[i]%md;
NTT(a,s,-1);
for(i=1;i<=mid-l+1;i++) f[i+mid]=(f[i+mid]+a[i+mid-l])%md;
cdq(mid+1,r);
}
}
int main()
{
LL n,i,j;
scanf("%lld",&n);
LL ln=1;
for(;ln<n;ln*=2);
q[0]=1,ny[0]=1;
for(i=1;i<=ln*2;i++) q[i]=q[i-1]*i%md;
ny[ln*2]=ksm(q[ln*2],md-2);
for(i=ln*2-1;i;i--) ny[i]=ny[i+1]*(i+1)%md;
for(i=1;i<=ln*2;i++) g[i]=ksm(2,1ll*i*(i-1)/2);
for(i=1;i<=ln*2;i++) nn[i]=ksm(i,md-2);
for(i=1;i<=ln*2;i*=2) ws[i]=ksm(3,(md-1)/i),wt[i]=ksm(ws[i],md-2);
cdq(1,ln);
printf("%lld",f[n]);
return 0;
}