bzoj 3456 城市規劃——分治FFT
阿新 • • 發佈:2018-11-30
題目:https://www.lydsy.com/JudgeOnline/problem.php?id=3456
設 dp[ i ] 表示 i 個點時連通的方案數。
考慮算補集:連通的方案數 == 隨便連方案數 - 不連通方案數
不連通方案數就和很久之前做過的“地震後的幻想鄉”一樣,列舉一個連通的點集,其中需要一直包含一個“劃分點”保證不重複;其餘部分隨便連。注意還有從 i 個點裡選 j 個點作為連通點集的那個組合數。
\( dp[i]=2^{C^{2}_{i}} - \sum\limits^{i-1}_{j=1} dp[j]*C^{j-1}_{i-1}*2^{C^{2}_{i-j}} \)
\( dp[i]=2^{C^{2}_{i}} - (i-1)!\sum\limits^{i-1}_{j=1} ( dp[j]*\frac{1}{(j-1)!} )( 2^{C^{2}_{i-j}}*\frac{1}{(i-j)!} ) \)
就可以分治FFT啦!
一開始還記得指數是對 mod-1 取模,後來就忘了,調了好久……注意 mod-1 不是質數,且是偶數,所以2沒有逆元,算 C 的時候直接 /2 就行了。
在 L==R 的地方把 f [ ] 弄好。雖然也可以給 f 賦上 \( C^{2}_{i} \) 的初值,然後卷積的時候每次 -=\( (i-1)!*a[j] \),就不用在 L==R 的時候特殊判斷了,但那樣可能因為總要乘 \( (i-1)! \),會變慢。
#include<iostream> #include<cstdio> #include<cstring> #includeView Code<algorithm> #define ll long long using namespace std; const int N=130005,M=N<<1,mod=1004535809,m2=mod-1;//N<<1 int n,len,r[M],f[M],g[M],a[M],b[M],jc[N],jcn[N],inv2; int rdn() { int ret=0;bool fx=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();} while(ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar(); return fx?ret:-ret; } void upd(int &x){x>=mod?x-=mod:0;} int pw(int x,int k,int md=mod) {int ret=1;while(k){if(k&1)ret=(ll)ret*x%md;x=(ll)x*x%md;k>>=1;}return ret;} int C(int n)///////%mod-1//// {return (ll)n*(n-1)/2%m2;}// /2 void init() { ////// inv2=pw(2,m2-2,m2);//////m2!!!! m2 is not prime!!!!! jc[1]=1;for(int i=2;i<n;i++)jc[i]=(ll)jc[i-1]*i%mod; jcn[n-1]=pw(jc[n-1],mod-2); for(int i=n-2;i>=0;i--)jcn[i]=(ll)jcn[i+1]*(i+1)%mod;//>=0 for(int i=1;i<n;i++)g[i]=(ll)pw(2,C(i))*jcn[i]%mod; } void ntt(int *a,bool fx) { for(int i=0;i<len;i++) if(i<r[i])swap(a[i],a[r[i]]); for(int R=2;R<=len;R<<=1) { int Wn=pw( 3,fx?(mod-1)-(mod-1)/R:(mod-1)/R ); for(int i=0,m=R>>1;i<len;i+=R) for(int j=0,w=1;j<m;j++,w=(ll)w*Wn%mod) { int x=a[i+j], y=(ll)w*a[i+m+j]%mod; a[i+j]=x+y; upd(a[i+j]); a[i+m+j]=x+mod-y; upd(a[i+m+j]); } } if(!fx)return; int inv=pw(len,mod-2); for(int i=0;i<len;i++)a[i]=(ll)a[i]*inv%mod; } void solve(int L,int R) { if(L==R){f[L]=(pw(2,C(L))-(ll)jc[L-1]*f[L])%mod+mod;upd(f[L]);return;} int mid=L+R>>1; solve(L,mid); int d=R-L,i,j; for(len=1;len<d;len<<=1); for(i=0;i<len;i++)r[i]=(r[i>>1]>>1)+((i&1)?len>>1:0); for(i=0,j=L;j<=mid;i++,j++)a[i]=(ll)f[j]*jcn[j-1]%mod;//jcn for(;i<len;i++)a[i]=0; for(i=0,j=R-L;i<=j;i++)b[i]=g[i+1]; for(;i<len;i++)b[i]=0; ntt(a,0); ntt(b,0); for(i=0;i<len;i++)a[i]=(ll)a[i]*b[i]%mod; ntt(a,1); for(int i=mid+1,j=i-L-1;i<=R;i++,j++)f[i]+=a[j],upd(f[i]); solve(mid+1,R); } int main() { n=rdn(); init(); solve(1,n); printf("%d\n",f[n]); return 0; }