城市规划

题意:让你求n个点所构成的无向连通图的方案数,n<=130000

解析:我么考虑一下推式子

设f[i]为i个点所构成的无向连通图的个数

g[i]为i个点所构成的无向图的个数

我们发现

\\g[n]=2^{C_{n}^{2}} \\g[n]=\sum_{i=1}^{n}C_{n-1}^{i-1}*f[i]*g[n-i]

然后还是展开组合数

\frac{g[n]}{(n-1)!}=\sum_{i=1}^{n}\frac{f[i]}{(i-1)!}*\frac{g[n-i]}{(n-i)!}

然后我们把他们看做

A(x)=B(x)C(x)

然后我们可以直接多项式求拟了

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=6e5+10;
const int N=2e5;
const ll mod=1004535809;
ll n,a[MAXN],b[MAXN],g[MAXN],f[MAXN],fac[MAXN],inv[MAXN];
int len,k;
int rev[MAXN];
ll ksm(ll x,ll y)
{
	ll ans=1;
	for (;y;y>>=1,x=(x*x)%mod) if (y&1) ans=(ans*x)%mod;
	return ans;
}
void init()
{
	fac[0]=1;
	for (int i=1;i<=N;i++) fac[i]=(fac[i-1]*(ll)i)%mod;
	inv[0]=inv[1]=1;
	for (int i=2;i<=N;i++) inv[i]=((mod-mod/i)*inv[mod%i])%mod;
	for (int i=2;i<=N;i++) inv[i]=(inv[i-1]*inv[i])%mod;	
}
void NTT(ll *a,int len,int t)
{
	for (int i=0;i<len;i++) if (i<rev[i]) swap(a[i],a[rev[i]]);
    for (int i=1;i<len;i<<=1)
    {
    	int s=(i<<1);
    	ll wn=ksm(3,(mod-1)/s);
    	if (t==-1) wn=ksm(wn,mod-2);
    	for (int j=0;j<len;j+=s)
    	{
    		ll w=1;
    		for (int k=j;k<j+i;k++)
    		{
    			ll x=a[k]; ll y=a[k+i]*w%mod;
    			a[k]=(x+y)%mod; a[k+i]=(x-y+mod)%mod;
    			w=(w*wn)%mod;
    		}
    	}
    }
    if (t==-1)
    {
    	ll w=ksm(len,mod-2);
    	for (int i=0;i<len;i++) a[i]=(a[i]*w)%mod;
    }
}
void la(int s)
{
	k=0;
	for (len=1;len<=s*2-1;len<<=1) k++;
	for (int i=0;i<len;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(k-1));
	NTT(a,len,1); NTT(b,len,1);
	for (int i=0;i<len;i++) a[i]=(a[i]*b[i])%mod;
	NTT(a,len,-1);	
}
void lalala(int n,int s)
{
	for (int i=0;i<(n<<1);i++) a[i]=b[i]=0;
	for (int i=0;i<s;i++) a[i]=g[i];
	for (int i=0;i<s;i++) b[i]=g[i];
	la(s);	
    for (int i=0;i<n;i++) b[i]=f[i];
    la(n);
    for (int i=0;i<n;i++) g[i]=(g[i]*2%mod-a[i]+mod)%mod;
}
void solve(int n)
{
	if (n==1){
        g[0]=ksm(f[0],mod-2);								
	}
	else {
		if (n&1) solve(n/2+1); else solve(n/2);
		lalala(n,(n&1)?n/2+1:n/2);
	}
}
int main()
{
	scanf("%lld",&n);
	init();
	for (int i=0;i<=n;i++) f[i]=ksm(2,((ll)i*(i-1)/2)%(mod-1))*inv[i]%mod;  
	solve(n+1);
    for (int i=0;i<=(n<<2);i++) f[i]=0;
    f[0]=0;
    for (int i=1;i<=n;i++) f[i]=ksm(2,((ll)i*(i-1)/2)%(mod-1))*inv[i-1]%mod;	
    k=0;
    for (len=1;len<=n*2;len<<=1) k++;	
    for (int i=0;i<len;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(k-1));
    NTT(g,len,1); NTT(f,len,1);	  
    for (int i=0;i<len;i++) g[i]=(g[i]*f[i])%mod;
    NTT(g,len,-1);
    if (n==0) cout << 1 << endl; else cout << g[n]*fac[n-1]%mod << endl;
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值