[bzoj3456]城市规划:多项式,分治

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 }
View Code
posted @ 2019-12-09 17:53  DeepinC  阅读(173)  评论(0编辑  收藏  举报