bzoj3456 城市规划 多项式求逆

28 篇文章 0 订阅
13 篇文章 0 订阅

Description


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

方案数 mod 1004535809.
对于 100%的数据, n <= 130000

Solution


之前似乎做过求给定连通块数量的dp,这个也是类似吧
令f[i]表示i个点形成连通块的方案数,有 f [ i ] = 2 C ( i , 2 ) − ∑ j = 1 i − 1 C ( i − 1 , j − 1 ) × f [ j ] × 2 C ( i − j , 2 ) f[i]=2^{C(i,2)}-\sum_{j=1}^{i-1}C(i-1,j-1)\times f[j]\times 2^{C(i-j,2)} f[i]=2C(i,2)j=1i1C(i1,j1)×f[j]×2C(ij,2)
大概就是容斥,总的方案数减去不符合的方案数,j用来枚举点1所在连通块的大小,剩下的随便连
我们令 w n = 2 C ( n , 2 ) w_n=2^{C(n,2)} wn=2C(n,2),拆一下柿子可以发现 f [ i ] = w i − ∑ j = 1 i − 1 f [ j ] × ( i − 1 ) ! ( j − 1 ) ! × w i − j ( i − j ) ! f[i]=w_i-\sum_{j=1}^{i-1}\frac{f[j]\times(i-1)!}{(j-1)!}\times \frac{w_{i-j}}{(i-j)!} f[i]=wij=1i1(j1)!f[j]×(i1)!×(ij)!wij
两边同时除(i-1)!得到 f [ i ] ( i − 1 ) ! = w i ( i − 1 ) ! − ∑ j = 1 i − 1 f [ j ] ( j − 1 ) ! × w i − j ( i − j ) ! \frac{f[i]}{(i-1)!}=\frac{w_i}{(i-1)!}-\sum_{j=1}^{i-1}\frac{f[j]}{(j-1)!}\times\frac{w_{i-j}}{(i-j)!} (i1)!f[i]=(i1)!wij=1i1(j1)!f[j]×(ij)!wij
移项得到 w i ( i − 1 ) ! = ∑ j = 1 i f [ j ] ( j − 1 ) ! × w i − j ( i − j ) ! \frac{w_i}{(i-1)!}=\sum_{j=1}^{i}\frac{f[j]}{(j-1)!}\times \frac{w_{i-j}}{(i-j)!} (i1)!wi=j=1i(j1)!f[j]×(ij)!wij
分别令三坨东西为三个多项式A B C,得到 A = B ∗ C ⇔ B = A ∗ C − 1 A=B*C\Leftrightarrow B=A*C^{-1} A=BCB=AC1,求出多项式C(x)的逆元即可

多项式求逆:
假定我们已经求出了多项式 A ( x ) A(x) A(x)在模 x n 2 x^{\frac{n}{2}} x2n意义下的逆元多项式 B ( x ) B(x) B(x),现在要求多项式 A ( x ) A(x) A(x)在模 x n x^{n} xn意义下的逆元多项式 G ( x ) G(x) G(x)
根据定义可得
A ( x ) ∗ B ( x ) ≡ 1 ( m o d x n 2 ) A(x)*B(x)\equiv 1\pmod {x^{\frac{n}{2}}} A(x)B(x)1(modx2n)
A ( x ) ∗ G ( x ) ≡ 1 ( m o d x n ) A(x)*G(x)\equiv 1\pmod {x^n} A(x)G(x)1(modxn)
那么两式相减(考虑同余的性质,可以撕烤一下为什么是这样
B ( x ) − G ( x ) ≡ 0 ( m o d x n 2 ) B(x)-G(x)\equiv 0\pmod {x^{\frac{n}{2}}} B(x)G(x)0(modx2n)
平方得到 B 2 ( x ) + G 2 ( x ) − 2 B ( x ) G ( x ) ≡ 0 ( m o d x n ) B^2(x)+G^2(x)-2B(x)G(x)\equiv 0\pmod {x^n} B2(x)+G2(x)2B(x)G(x)0(modxn)
左右同时乘 A ( x ) A(x) A(x)得到 G ( x ) ≡ 2 B ( x ) − A B 2 ( x ) ( m o d x n ) G(x)\equiv 2B(x)-AB^2(x) \pmod{x^n} G(x)2B(x)AB2(x)(modxn) 注意这里全部变成 x n x^n xn同余下了,只能消去此时的 G ( x ) G(x) G(x)
这样就能用递归(or倍增 的方法搞定多项式求逆了,注意把多项式补齐为2的次幂项

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>

typedef long long LL;
const int MOD=1004535809;
const int N=300005;
const int g=3;

LL rev[N],tmp[N],fac[N],ny[N],a[N],c[N],nc[N];
LL nyn;

LL ksm(LL x,LL dep) {
	LL ret=1;
	while (dep) {
		if (dep&1) ret=ret*x%MOD;
		x=x*x%MOD; dep/=2;
	}
	return ret;
}

void NTT(LL *a,int len,int f) {
	int lg=log(len)/log(2)+0.1;
	for (int i=0;i<len;i++) {
		rev[i]=(rev[i/2]/2)|((i&1)<<(lg-1));
		if (i<rev[i]) std:: swap(a[i],a[rev[i]]);
	}
	for (int i=1;i<len;i*=2) {
		LL wn=ksm(g,(MOD-1)/i/2);
		if (f==-1) wn=ksm(g,(MOD-1)-(MOD-1)/i/2);
		for (int j=0;j<len;j+=i*2) {
			LL w=1;
			for (int k=0;k<i;k++) {
				int u=a[j+k],v=a[j+i+k]*w%MOD;
				a[j+k]=(u+v)%MOD; a[j+i+k]=(u-v+MOD)%MOD;
				w=wn*w%MOD;
			}
		}
	}
	if (f==-1) {
		LL nnyy=ksm(len,MOD-2);
		for (int i=0;i<len;i++) a[i]=a[i]*nnyy%MOD;
	}
}

void get_ny(LL *a,LL *b,int n) {
	if (n==1) {
		b[0]=ksm(a[0],MOD-2);
		return ;
	}
	get_ny(a,b,n/2);
	memcpy(tmp,a,sizeof(a[0])*n);
	memset(tmp+n,0,sizeof(tmp[0])*n);
	NTT(tmp,n*2,1); NTT(b,n*2,1);
	for (int i=0;i<n*2;i++) tmp[i]=((LL)b[i]%MOD*(2-tmp[i]*b[i]%MOD+MOD)%MOD)%MOD;
	NTT(tmp,n*2,-1);
	for (int i=0;i<n;i++) b[i]=tmp[i];
	memset(b+n,0,sizeof(b[0])*n);
}

int main(void) {
	int n; scanf("%d",&n);
	int len; for (len=1;len<=n;len*=2);
	fac[0]=ny[0]=1;
	for (int i=1;i<=n;i++) fac[i]=fac[i-1]*(LL)i%MOD,ny[i]=ksm(fac[i],MOD-2);
	for (int i=0;i<=n;i++) c[i]=ksm(2,((LL)(i-1)*i/2)%(MOD-1))*ny[i]%MOD;
	for (int i=1;i<=n;i++) a[i]=ksm(2,((LL)(i-1)*i/2)%(MOD-1))*ny[i-1]%MOD;
	get_ny(c,nc,len);
	NTT(a,len*2,1); NTT(nc,len*2,1);
	for (int i=0;i<len*2;i++) a[i]=a[i]*nc[i]%MOD;
	NTT(a,len*2,-1);
	printf("%lld\n", a[n]*fac[n-1]%MOD);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值