[BZOJ3456]城市规划(生成函数+多项式求逆+多项式求ln)
2018-04-01 18:05
330 查看
城市规划
时间限制:40s 空间限制:256MB
题目描述
刚刚解决完电力网络的问题, 阿狸又被领导的任务给难住了.
刚才说过, 阿狸的国家有n个城市, 现在国家需要在某些城市对之间建立一些贸易路线, 使得整个国家的任意两个城市都直接或间接的连通. 为了省钱, 每两个城市之间最多只能有一条直接的贸易路径. 对于两个建立路线的方案, 如果存在一个城市对, 在两个方案中是否建立路线不一样, 那么这两个方案就是不同的, 否则就是相同的. 现在你需要求出一共有多少不同的方案.
好了, 这就是困扰阿狸的问题. 换句话说, 你需要求出n个点的简单(无重边无自环)无向连通图数目.
由于这个数字可能非常大, 你只需要输出方案数mod 1004535809(479 * 2 ^ 21 + 1)即可.
输入格式
仅一行一个整数n(<=130000)
输出格式
仅一行一个整数, 为方案数 mod 1004535809.
样例输入
3样例输出
4提示
对于 100%的数据, n <= 130000
题目来源
没有写明来源
传说中FFT应用的最高境界?现在恐怕配不上这个称号了。
http://blog.miskcoo.com/2015/05/bzoj-3456
解法一:
先按照惯例减弱条件限制,不要求图连通,再将连通和不联通之间的递推关系式化成卷积的形式。最后直接上生成函数,在模$x^{n+1}$下求多项式的逆元即可。
次数界放宽!递归求逆元的时候次数界要放成两倍!
#include<cstdio> #include<algorithm> #define rep(i,l,r) for (int i=l; i<=r; i++) using namespace std; const int N=300100,mod=1004535809,g=3; int n,m,a ,b ,lg[N<<1],fac ,fin ,tmp ,c ,b1 ,rev ; int ksm(int a,int b){ int res; for (res=1; b; a=1ll*a*a%mod,b>>=1) if (b&1) res=1ll*res*a%mod; return res; } void DFT(int a[],int n,int f){ for (int i=0; i<n; i++) if (i<rev[i]) swap(a[i],a[rev[i]]); for (int i=1; i<n; i<<=1){ int wn=ksm(g,(f==1) ? (mod-1)/(i<<1) : (mod-1)-(mod-1)/(i<<1)); for (int p=i<<1,j=0; j<n; j+=p){ int w=1; for (int k=0; k<i; k++,w=1ll*w*wn%mod){ int x=a[j+k],y=1ll*w*a[i+j+k]%mod; a[j+k]=(x+y)%mod; a[i+j+k]=(x-y+mod)%mod; } } } if (f==1) return; int inv=ksm(n,mod-2); for (int i=0; i<n; i++) a[i]=1ll*a[i]*inv%mod; } void get(int a[],int b[],int l){ if (l==1){ b[0]=ksm(a[0],mod-2); return ;} get(a,b,l>>1); int n=l<<1; for (int i=0; i<l; i++) tmp[i]=a[i],tmp[i+l]=0; for (int i=0; i<n; i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(lg -1)); DFT(tmp,n,1); DFT(b,n,1); for (int i=0; i<n; i++) tmp[i]=1ll*b[i]*(2-1ll*tmp[i]*b[i]%mod+mod)%mod; DFT(tmp,n,-1); for (int i=0; i<l; i++) b[i]=tmp[i],b[i+l]=0; } int main(){ freopen("bzoj3456.in","r",stdin); freopen("bzoj3456.out","w",stdout); scanf("%d",&n); fac[0]=fin[0]=1; lg[1]=0; rep(i,2,n<<2) lg[i]=lg[i>>1]+1; rep(i,1,n) fac[i]=1ll*fac[i-1]*i%mod; fin =ksm(fac ,mod-2); for (int i=n-1; i; i--) fin[i]=1ll*fin[i+1]*(i+1)%mod; rep(i,0,n) b[i]=1ll*ksm(2,1ll*i*(i-1)/2%(mod-1))*fin[i]%mod; rep(i,1,n) c[i]=1ll*ksm(2,1ll*i*(i-1)/2%(mod-1))*fin[i-1]%mod; for (m=1; m<=n; m<<=1); get(b,b1,m); DFT(c,m<<1,1); DFT(b1,m<<1,1); for (int i=0; i<(m<<1); i++) c[i]=1ll*c[i]*b1[i]%mod; DFT(c,m<<1,-1); printf("%lld\n",1ll*c *fac[n-1]%mod); return 0; }
解法二:指数型生成函数
惊奇地发现正好要求的函数可以放在exp的指数上,多项式求ln即可。
形式幂级数什么的真是太有意思了。
https://www.geek-share.com/detail/2698671739.html
#include<cstdio> #include<algorithm> #define rep(i,l,r) for (int i=l; i<=r; i++) using namespace std; const int N=600100,mod=1004535809,g=3; int n,m,G ,G1 ,invG ,lg[N<<1],fac ,fin ,tmp ,c ,b1 ,rev ; int ksm(int a,int b){ int res; for (res=1; b; a=1ll*a*a%mod,b>>=1) if (b&1) res=1ll*res*a%mod; return res; } void DFT(int a[],int n,int f){ for (int i=0; i<n; i++) if (i<rev[i]) swap(a[i],a[rev[i]]); for (int i=1; i<n; i<<=1){ int wn=ksm(g,(f==1) ? (mod-1)/(i<<1) : (mod-1)-(mod-1)/(i<<1)); for (int p=i<<1,j=0; j<n; j+=p){ int w=1; for (int k=0; k<i; k++,w=1ll*w*wn%mod){ int x=a[j+k],y=1ll*w*a[i+j+k]%mod; a[j+k]=(x+y)%mod; a[i+j+k]=(x-y+mod)%mod; } } } if (f==1) return; int inv=ksm(n,mod-2); for (int i=0; i<n; i++) a[i]=1ll*a[i]*inv%mod; } void get(int a[],int b[],int l){ if (l==1){ b[0]=ksm(a[0],mod-2); return ;} get(a,b,l>>1); int n=l<<1; for (int i=0; i<l; i++) tmp[i]=a[i],tmp[i+l]=0; for (int i=0; i<n; i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(lg -1)); DFT(tmp,n,1); DFT(b,n,1); for (int i=0; i<n; i++) tmp[i]=1ll*b[i]*(2-1ll*tmp[i]*b[i]%mod+mod)%mod; DFT(tmp,n,-1); for (int i=0; i<l; i++) b[i]=tmp[i],b[i+l]=0; } int main(){ freopen("bzoj3456.in","r",stdin); freopen("bzoj3456.out","w",stdout); scanf("%d",&n); fac[0]=fin[0]=1; lg[1]=0; rep(i,2,n<<3) lg[i]=lg[i>>1]+1; rep(i,1,n) fac[i]=1ll*fac[i-1]*i%mod; fin =ksm(fac ,mod-2); for (int i=n-1; i; i--) fin[i]=1ll*fin[i+1]*(i+1)%mod; G[0]=G[1]=1; G1 =0; rep(i,0,n) G[i]=1ll*ksm(2,(1ll*i*(i-1)/2)%(mod-1))*fin[i]%mod; rep(i,1,n) G1[i-1]=1ll*G[i]*i%mod; for (m=1; m<=n; m<<=1); get(G,invG,m); m<<=1; DFT(G1,m,1); DFT(invG,m,1); for (int i=0; i<m; i++) G1[i]=1ll*invG[i]*G1[i]%mod; DFT(G1,m,-1); printf("%lld\n",(1ll*G1[n-1]*ksm(n,mod-2)%mod)*fac %mod); return 0; }
相关文章推荐
- bzoj3456 城市规划【生成函数+多项式求逆元】
- BZOJ 3456: 城市规划 [多项式求逆元 组合数学 | 生成函数 多项式求ln]
- BZOJ3456:城市规划 (多项式求逆/分治+NTT)
- 【XSY1332】【BZOJ3456】轩辕朗的城市规划 无向连通图计数 CDQ分治 FFT 多项式求逆 多项式ln
- BZOJ3456 城市规划 【多项式求ln】
- 【BZOJ 3456】 3456: 城市规划 (NTT+多项式求逆)
- bzoj 3456 城市规划 无向简单连通图个数 多项式求逆
- [BZOJ3456] 城市规划 - 快速傅里叶变换 - 快速数论变换 - 卷积 - 多项式求逆
- [带标号无向连通图计数 容斥原理 多项式求逆 多项式求ln 模板题] BZOJ 3456 城市规划
- bzoj 3456: 城市规划 (NTT+多项式求逆)
- BZOJ3456 城市规划 【多项式求逆】
- 【多项式求逆】[BZOJ3456]城市规划
- [BZOJ2259]异化多肽(生成函数+NTT+多项式求逆)
- 【BZOJ】3456: 城市规划(多项式求ln)
- BZOJ 3456 城市规划 多项式求ln
- [多项式ln][多项式exp][多项式求幂][生成函数][DP][FNT] BZOJ 3684: 大朋友和多叉树
- [BZOJ3456] [多项式求逆] 城市规划
- [多项式ln][多项式exp][背包DP][生成函数] LOJ #556. 咱们去烧菜吧
- 【BZOJ3456】城市规划 多项式求逆
- 【BZOJ】3456: 城市规划 动态规划+多项式求逆