Luogu4841 [集训队作业2013]城市规划

Luogu4841 [集训队作业2013]城市规划

终于弄懂啦!

指数型生成函数(\(EGF\)

\(a_i\)表示\(i\)个点的有标号无向连通图数量,建立数列\(\{a\}\)\(EGF\)\(f\)

同时我们记录\(b_i\)表示个点的有标号无向图数量(显然\(b_i=2^{{i \choose 2}}\)),设\(g\)为其\(EGF\)

我们先给出它们之间的关系:

\[g=\sum_{k=0}^{\infty} \frac{f^k}{k!} \]

考虑其组合意义,有标号无向图由多个有标号无向连通图组成,那么我们可以设这些有标号无向连通图的大小为一个数列\(c_{1 \cdots k}\)

我们对于一个给定的数列,可以给出一个计算方案。

\[{n \choose c_1,c_2,\cdots,c_k} \prod a_i\\ =\frac{n!}{c_1!c_2!\cdots c_k!} \prod a_i \]

也就是把编号先分配到每一个连通块,然后在各自连通块中计算答案。

我们会发现,这个数列在式子中对应的那一项正好就是\(\frac{n!}{c_1!c_2!\cdots c_k!} \prod a_i\),但是外面多除了一个\(k!\),这是为什么呢?

我们考虑一个\(c\)数组的一个排列,就会发现它们的本质是相同的,只不过选择顺序不同,所以我们需要除以\(k!\)来取消它们的顺序关系(当\(c\)中有重复元素时,可以手模一下,发现不会影响计算,也就是一个集合只会被计算一次)。

\[g=\sum_{k=0}^{\infty} \frac{f^k}{k!} \]

\(Wow!\)麦克劳林级数

\[g=e^f\\ f=\ln g\\ g_i=\frac{2^{i \choose 2}}{i!} \]

算出\(g\)数组,取一个\(\ln\),做完了。

\(Code:\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 400005
#define ll long long
using namespace std;
const int p=1004535809;
int n,s,l,inv[N],fac[N],infac[N],rev[N];
int f[N],g[N],a[N],b[N],c[N],d[N],G[2][25];
void Add(int &x,int y)
{
    x=(x+y)%p;
}
void Del(int &x,int y)
{
    x=(x-y)%p;
}
void Mul(int &x,int y)
{
    x=(ll)x*y%p;
}
int add(int x,int y)
{
    return (x+y)%p;
}
int del(int x,int y)
{
    return (x-y)%p;
}
int mul(int x,int y)
{
    return (ll)x*y%p;
}
int ksm(int x,int y)
{
    int ans=1;
    while (y)
    {
        if (y & 1)
            Mul(ans,x);
        Mul(x,x);
        y >>=1;
    }
    return ans;
}
void Pre()
{
    G[0][21]=ksm(3,(p-1)/(1 << 21));
    G[1][21]=ksm(G[0][21],p-2);
    for (int i=20;i;--i)
    {
        G[0][i]=mul(G[0][i+1],G[0][i+1]);
        G[1][i]=mul(G[1][i+1],G[1][i+1]);
    }
}
void solve(int n)
{
    s=1,l=0;
    while (s<n)
        s <<=1,++l;
    for (int i=1;i<s;++i)
        rev[i]=(rev[i >> 1] >> 1) | ((i & 1) << l-1);
}
void NTT(int *a,int t)
{
    for (int i=0;i<s;++i)
        if (i<rev[i])
            swap(a[i],a[rev[i]]);
    for (int mid=1,o=1;mid<s;mid <<=1,++o)
        for (int j=0;j<s;j+=mid << 1)
        {
            int g=1;
            for (int k=0;k<mid;++k,Mul(g,G[t][o]))
            {
                int x=a[j+k],y=mul(g,a[j+k+mid]);
                a[j+k]=add(x,y),a[j+k+mid]=del(x,y);
            }
        }
}
void GetInv(int *f,int *g,int R)
{
    if (R==2)
    {
        g[0]=ksm(f[0],p-2);
        return;
    }
    GetInv(f,g,R >> 1);
    memcpy(c,g,R*sizeof(int)),memcpy(d,f,(R >> 1)*sizeof(int));
    solve(R);
    NTT(c,0),NTT(d,0);
    for (int i=0;i<s;++i)
        c[i]=del(add(c[i],c[i]),mul(d[i],mul(c[i],c[i])));
    NTT(c,1);
    for (int i=0;i<s;++i)
        Mul(c[i],inv[s]);
    memcpy(g,c,(R >> 1)*sizeof(int));
    memset(c,0,R*sizeof(int)),memset(d,0,R*sizeof(int));
}
void Dev(int *a,int *b,int n)
{
    for (int i=1;i<n;++i)
        b[i-1]=mul(a[i],i);
    b[n-1]=0;
}
void InvDev(int *a,int *b,int n)
{
    b[0]=0;
    for (int i=0;i<n;++i)
        b[i+1]=mul(inv[i+1],a[i]);
}
void Cut(int *a,int n,int s)
{
    for (int i=n;i<s;++i)
        a[i]=0;
}
void GetLn(int *f,int *g,int n)
{
    s=1,l=0;
    while (s<n)
        s <<=1,++l;
    s <<=1;
    GetInv(f,a,s);
    Cut(f,n,s);
    Dev(f,b,n);
    solve(n << 1);
    NTT(a,0),NTT(b,0);
    for (int i=0;i<s;++i)
        Mul(a[i],b[i]);
    NTT(a,1);
    for (int i=0;i<s;++i)
        Mul(a[i],inv[s]);
    InvDev(a,g,n);
    Cut(g,n,s);
}
int main()
{
    inv[1]=1,fac[0]=fac[1]=infac[0]=infac[1]=1;
    for (int i=2;i<=400000;++i)
        inv[i]=mul(p-p/i,inv[p%i]),fac[i]=mul(fac[i-1],i),infac[i]=mul(infac[i-1],inv[i]);
    Pre();
    scanf("%d",&n);
    ++n;
    for (int i=0;i<n;++i)
        g[i]=mul(ksm(2,((ll)i*(i-1) >> 1)%(p-1)),infac[i]);
    GetLn(g,f,n);
    --n;
    printf("%d\n",add(p,mul(f[n],fac[n])));
    return 0;
}
posted @ 2020-12-26 15:50  GK0328  阅读(45)  评论(0编辑  收藏  举报