Description
刚刚解决完电力网络的问题, 阿狸又被领导的任务给难住了. 刚才说过, 阿狸的国家有n个城市, 现在国家需要在某些城市对之间建立一些贸易路线, 使得整个国家的任意两个城市都直接或间接的连通. 为了省钱, 每两个城市之间最多只能有一条直接的贸易路径. 对于两个建立路线的方案, 如果存在一个城市对, 在两个方案中是否建立路线不一样, 那么这两个方案就是不同的, 否则就是相同的. 现在你需要求出一共有多少不同的方案. 好了, 这就是困扰阿狸的问题. 换句话说, 你需要求出n个点的简单(无重边无自环)无向连通图数目. 由于这个数字可能非常大, 你只需要输出方案数mod 1004535809(479 * 2 ^ 21 + 1)即可.n<=130000
物理学考是想FFT的好时间。
首先,按照习惯FFT的题先当成dp做。
设$f_i$表示有i个点的联通图有几个。
然后我们发现它不能转移。
于是我们设$g_i$表示有i个点的不联通图有几个。
$f_i+g_i=2^{\frac{i \times (i-1)}{2}}$
就是联不联通的情况都加起来就是所有的图,总数量就是讨论每一条边建不建。
然后我们要考虑怎么转移不会重复。一个比较简单的想法是去掉不联通图中的联通块,但是这样会重复。
多yy几分钟。于是就想到之前的常用技巧:钦定。
我们钦定编号最大的点所在的联通块。每次只去除这个联通块。这样就不重复了。
$g_i=\sum\limits_{j=1}^{i-1} f_j \times (f_{i-j} + g_{i-j}) \times C_{i-1}^{j-1}$
含义就是:枚举i号点所在的联通块大小为j,这个联通块的总方案数是$f_j$,剩余部分随意反正它已经不联通了,是$f_{i-j}+g_{i-j}$
然后再在除了i号点以外的$i-1$个点中选定具体是哪$j-1$个点和点i在同一个联通块里,是$C_{i-1}^{j-1}$
然后发现转移是带有依赖的,于是用分治FFT解决。
1 #include<cstdio> 2 #define int long long 3 #define mod 1004535809 4 #define S 131073 5 int a[S],b[S],h[S],fac[S],inv[S],g[S],len,n,rev[S]; 6 int pow(int b,int t,int a=1){for(;t;t>>=1,b=b*b%mod)if(t&1)a=a*b%mod;return a;} 7 int C(int b,int t){return fac[b]*inv[t]%mod*inv[b-t]%mod;} 8 void NTT(int *a,int opt){ 9 for(int i=1;i<len;++i)rev[i]=rev[i>>1]>>1|(i&1?len>>1:0); 10 for(int i=0;i<len;++i)if(rev[i]>i)a[i]^=a[rev[i]]^=a[i]^=a[rev[i]]; 11 for(int mid=1;mid<len;mid<<=1) 12 for(int i=0,t=pow(3,opt*(mod-1)/mid/2+mod-1);i<len;i+=mid<<1) 13 for(int j=0,w=1,x,y;j<mid;++j,w=w*t%mod) 14 x=a[i+j],y=a[i+j+mid]*w%mod,a[i+j]=(x+y)%mod,a[i+j+mid]=(x-y+mod)%mod; 15 if(opt==-1)for(int i=0,iv=pow(len,mod-2);i<len;++i)a[i]=a[i]*iv%mod; 16 } 17 void solve(int l,int r){ 18 if(l==r){g[l]=fac[l-1]*g[l]%mod;return;} 19 int mid=l+r>>1;solve(l,mid); 20 len=1;while(len<=r-l+1)len<<=1; 21 for(int i=0;i<len;++i)a[i]=b[i]=0; 22 for(int i=0;i<=r-l+1;++i)a[i]=h[i]*inv[i]%mod; 23 for(int i=l;i<=mid;++i)b[i-l]=(h[i]-g[i]+mod)*inv[i-1]%mod; 24 NTT(a,1);NTT(b,1); 25 for(int i=0;i<len;++i)a[i]=a[i]*b[i]%mod; 26 NTT(a,-1); 27 for(int i=mid+1;i<=r;++i)g[i]=(g[i]+a[i-l])%mod; 28 solve(mid+1,r); 29 } 30 signed main(){ 31 scanf("%lld",&n);fac[0]=1; 32 for(int i=1;i<=n;++i)fac[i]=fac[i-1]*i%mod; 33 inv[n]=pow(fac[n],mod-2); 34 for(int i=n-1;~i;--i)inv[i]=inv[i+1]*(i+1)%mod; 35 for(int i=1;i<=n;++i)h[i]=pow(2,i*(i-1)/2); 36 solve(1,n);printf("%lld\n",(h[n]-g[n]+mod)%mod); 37 }
原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/12012535.html