olahiuj的博客

想要再努力一点

bzoj3456 城市规划

Description


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

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

Solution


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

多项式求逆:
假定我们已经求出了多项式A(x)在模xn2意义下的逆元多项式B(x),现在要求多项式A(x)在模xn意义下的逆元多项式G(x)
根据定义可得
A(x)B(x)1(modxn2)
A(x)G(x)1(modxn)
那么两式相减(考虑同余的性质,可以撕烤一下为什么是这样
B(x)G(x)0(modxn)
平方得到B2(x)+G2(x)2B(x)G(x)0(modxn)
左右同时乘A(x)得到G(x)2B(x)AB2(x)(modxn) 注意这里全部变成xn同余下了,只能消去此时的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;
}
阅读更多
版权声明:虽然是个蒟蒻但是转载还是要说一声的哟 https://blog.csdn.net/jpwang8/article/details/79967190
文章标签: bzoj
上一篇bzoj4815 [cqoi2017]小Q的表格
下一篇jzoj5661 [GDOI2018Day1模拟4.17]药香沁鼻
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭