zoukankan      html  css  js  c++  java
  • 【洛谷P4841】城市规划

    题目

    题目链接:https://www.luogu.com.cn/problem/P4841
    刚刚解决完电力网络的问题,阿狸又被领导的任务给难住了。
    刚才说过,阿狸的国家有 (n) 个城市,现在国家需要在某些城市对之间建立一些贸易路线,使得整个国家的任意两个城市都直接或间接的连通。
    为了省钱, 每两个城市之间最多只能有一条直接的贸易路径。对于两个建立路线的方案,如果存在一个城市对,在两个方案中是否建立路线不一样,那么这两个方案就是不同的,否则就是相同的。现在你需要求出一共有多少不同的方案。
    好了,这就是困扰阿狸的问题。换句话说,你需要求出 (n) 个点的简单 (无重边无自环) 有标号无向连通图数目。
    由于这个数字可能非常大, 你只需要输出方案数对 (1004535809) ( (479 imes 2 ^{21} + 1) ) 即可。
    (nleq 130000)

    思路

    QuantAsk 和 my_dog 太强啦 orz。


    (f_i) 表示 (i) 个点的无向连通图数量,(g_i) 表示 (i) 个点的无向图数量。
    因为每一条边可选可不选,所以显然

    [g_i=2^{frac{n(n-1)}{2}}=2^{inom{n}{2}} ]

    考虑枚举点 (1) 所在联通块大小,不难得到

    [g_i=sum^{i}_{j=1}inom{i}{j}f_jg_{i-j} ]

    [2^{inom{n}{2}}=sum^{n}_{i=1}inom{n}{i}f_i imes 2^{inom{n-i}{2}} ]

    组合数拆开,移项

    [frac{2^{inom{n}{2}}}{(n-1)!}=sum^{n}_{i=1}frac{f_i}{(i-1)!} imes frac{2^{inom{n-i}{2}}}{(n-i)!} ]

    (F(x)=sum^{+infty}_{n=1}frac{2^{inom{n}{2}}}{(n-1)!})(G(x)=sum^{+infty}_{n=1}sum^{n}_{i=1}frac{f_i}{(i-1)!})(H(x)=sum^{n}_{i=0}frac{2^{inom{i}{2}}}{i!}),那么

    [F(x)=G(x)*H'(x) ]

    所以就把 (H(x)) 求逆再乘上 (F(x)) 即可。
    时间复杂度 (O(nlog n))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int N=280010,MOD=1004535809,G=3,Ginv=334845270;
    ll f[N],g[N],h[3][N],X[N],Y[N],fac[N],inv[N];
    int n,rev[N];
    
    ll fpow(ll x,ll k)
    {
    	ll ans=1;
    	for (;k;k>>=1,x=x*x%MOD)
    		if (k&1) ans=ans*x%MOD;
    	return ans;
    }
    
    void NTT(ll *f,int tag,int lim)
    {
    	for (int i=0;i<lim;i++)
    		if (i<rev[i]) swap(f[i],f[rev[i]]);
    	for (int k=1;k<lim;k<<=1)
    	{
    		ll tmp=fpow((tag==1)?G:Ginv,(MOD-1)/(k<<1));
    		for (int i=0;i<lim;i+=(k<<1))
    		{
    			ll w=1;
    			for (int j=0;j<k;j++,w=w*tmp%MOD)
    			{
    				ll x=f[i+j],y=w*f[i+j+k]%MOD;
    				f[i+j]=(x+y)%MOD; f[i+j+k]=(x-y+MOD)%MOD;
    			}
    		}
    	}
    }
    
    void Fmul(ll *f,ll *g,int lim)
    {
    	memset(X,0,sizeof(X));
    	memset(Y,0,sizeof(Y));
    	for (int i=0;i<(lim>>1);i++)
    		X[i]=f[i],Y[i]=g[i];
    	NTT(X,1,lim); NTT(Y,1,lim);
    	for (int i=0;i<lim;i++) X[i]=X[i]*Y[i]%MOD;
    	NTT(X,-1,lim);
    	ll inv=fpow(lim,MOD-2);
    	for (int i=0;i<lim;i++) X[i]=X[i]*inv%MOD;
    	memcpy(f,X,sizeof(X));
    }
    
    void Finv(ll *f,int n)
    {
    	h[0][0]=fpow(f[0],MOD-2);
    	int id=0;
    	for (int lim=4;lim<=n*4;lim<<=1)
    	{
    		id^=1;
    		for (int i=0;i<lim;i++)
    			rev[i]=(rev[i>>1]>>1)|((i&1)?(lim>>1):0);
    		memcpy(h[2],h[id^1],sizeof(h[2]));
    		Fmul(h[2],h[2],lim); Fmul(h[2],f,lim);
    		for (int i=0;i<(lim>>1);i++)
    			h[id][i]=((h[id^1][i]<<1)-h[2][i]+MOD)%MOD;
    	}
    	memcpy(f,h[id],sizeof(h[id]));
    }
    
    int main()
    {
    	scanf("%d",&n);
    	fac[0]=inv[0]=1;
    	for (int i=1;i<=n;i++) fac[i]=fac[i-1]*i%MOD;
    	inv[n]=fpow(fac[n],MOD-2);
    	for (int i=n-1;i>=1;i--) inv[i]=inv[i+1]*(i+1)%MOD;
    	for (int i=1;i<=n;i++) f[i]=fpow(2LL,1LL*i*(i-1)/2)*inv[i-1]%MOD;
    	for (int i=0;i<=n;i++) g[i]=fpow(2LL,1LL*i*(i-1)/2)*inv[i]%MOD;
    	Finv(g,n+1);
    	int lim=1;
    	while (lim<=2*n) lim<<=1;
    	for (int i=0;i<lim;i++)
    		rev[i]=(rev[i>>1]>>1)|((i&1)?(lim>>1):0);
    	Fmul(f,g,lim);
    	printf("%lld",f[n]*fac[n-1]%MOD);
    	return 0;
    }
    
  • 相关阅读:
    【Android 界面效果7】Android中Gallery和ImageSwitcher同步自动(滚动)播放图片库
    【Android 界面效果10】Android中View,ViewGroup,Window之间的关系
    Android平台移植初解
    Android实现网络多线程断点续传下载
    centos修改 机器名
    Linux简单地隐藏文件及显示隐藏文件
    tar 打包隐藏文件及排除不需要打包的文件
    mysql新建用户,用户授权,删除用户,修改密码
    Linux下查看磁盘剩余空间和文件夹大小
    VPS Centos 5.5 安装 kloxo面板
  • 原文地址:https://www.cnblogs.com/stoorz/p/14284000.html
Copyright © 2011-2022 走看看