JZOJ 3303. 【集訓隊互測2013】城市規劃(卷積+分治NTT)

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

題解

  • 這種計數題,按照常規的思路,先考慮一下遞推,
  • ii的答案爲fif_i,那麼fif_i可以由fj(1j&lt;i)f_j(1≤j&lt;i)怎麼轉移,
  • 經過思考後,發現好像不是很可行。
  • (也許可以,至少多數人都沒有推出來)
  • 那我們想想容斥(不要怕),用總方案數減去不可行的方案數(這就是容斥的全部),
  • 開始推一波式子:
  • fi=2Ci2j=1i1fjCi1j12Cij2f_i=2^{C_i^2}-\sum_{j=1}^{i-1}f_j*C_{i-1}^{j-1}*2^{C_{i-j}^2}
  • 2Ci22^{C_i^2}ii個點之間所有無向邊選或不選的總方案數;
  • j=1i1\sum_{j=1}^{i-1}是枚舉任意一個點(比如說11號點)所在的連通塊的大小;
  • fjf_j表示該連通塊組成的方案數(當點已經確定的情況下);
  • Ci1j1C_{i-1}^{j-1}是在剩餘點中選出與ii組成大小爲jj的連通塊的方案數;
  • 2Cij22_{C_{i-j}^2}是該連通塊以外的點之間所有邊選或不選的方案數;
  • 這樣可以保證不重不漏!!!
  • gig_i表示2Ci22^{C_i^2},也就是ii個點之間所有無向邊選或不選的總方案數,
  • fi=2Ci2j=1i1fjCi1j12Cij2f_i=2^{C_i^2}-\sum_{j=1}^{i-1}f_j*C_{i-1}^{j-1}*2^{C_{i-j}^2}
  • =gij=1i1fjCi1j1gij=g_i-\sum_{j=1}^{i-1}f_j*C_{i-1}^{j-1}*g_{i-j}
  • =gij=1i1fj(i1)!(j1)!(ij)!gij=g_i-\sum_{j=1}^{i-1}f_j*\frac{(i-1)!}{(j-1)!(i-j)!}*g_{i-j}
  • =gi(i1)!j=1i1fj(j1)!gij(ij)!=g_i-(i-1)!\sum_{j=1}^{i-1}\frac{f_j}{(j-1)!}*\frac{g_{i-j}}{(i-j)!}
  • 右邊顯然是個卷積的形式,用NTTNTT來做,
  • 但是要做nn次,那麼用分治NTTNTT.

代碼

#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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章