zoukankan      html  css  js  c++  java
  • 洛谷 P4233 射命丸文的笔记

    如果一个竞赛图含有哈密顿回路,则称这张竞赛图为值得记录的。

    从所有含有(i)个顶点(顶点互不相同)的,值得记录的竞赛图中等概率随机选取一个。

    求所有(iin [1,n])选取的竞赛图中哈密顿回路数量的期望值。

    题目让你求所有有哈密顿回路的竞赛图中哈密顿回路个数的期望

    (n)个点的哈密顿回路是比较好求的,考虑钦定选(n)个点组成圆排列,其余的点可以随便选,那么个数就是((n-1)!2^{frac{n(n-1)}{2}-n})

    那么我们就需要求所有有哈密顿回路的竞赛图,也就是强连通竞赛图

    我们先考虑dp,设(g_n)表示(n)个点的竞赛图个数,(f_n)表示(n)个点的强连通竞赛图个数

    我们发现竞赛图缩完点之后一定是一条链,那么可以枚举缩点后拓扑序最小的强连通分量的大小(i),这个强连通分量必然只能和其他(n-i)个点连边,那么就有

    [g_n=sum_{i=1}^ninom{n}{i}f_ig_{n-i} ]

    而如果我们设(g_0=1,f_0=0),就可以把上式看成两个EGF的卷积,也就是

    [G(x)=F(x)G(x)+1 ]

    [F(x)=1-frac{1}{G(x)} ]

    直接多项式求逆就可以了

    Code

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    const int N = 4e5;
    const int p = 998244353;
    using namespace std;
    int n,maxn,lg,rev[N + 5],G[N + 5][2],a[N + 5],ans[N + 5],fac[N + 5];
    int mypow(int a,long long x){int s = 1;for (;x;x & 1 ? s = 1ll * s * a % p : 0,a = 1ll * a * a % p,x >>= 1);return s;}
    void pre(int n)
    {
        maxn = 1,lg = 0;
        while (maxn <= n)
            maxn <<= 1,lg++;
        for (int i = 0;i < maxn;i++)
            rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << lg - 1);
    }
    void clear(int *a,int n,int l = 0)
    {
        for (int i = l;i < n;i++)
            a[i] = 0;
    }
    void ntt(int *a,int typ)
    {
        for (int i = 0;i < maxn;i++)
            if (i < rev[i])
                swap(a[i],a[rev[i]]);
        for (int i = 1;i < maxn;i <<= 1)
            for (int j = 0;j < maxn;j += i << 1)
                for (int k = 0;k < i;k++)
                {
                    int x = a[j + k],t = 1ll * G[k + i][typ] * a[j + k + i] % p;
                    a[j + k] = (x + t) % p;
                    a[j + k + i] = (x - t) % p;
                }
        if (!typ)
        {
            int inv = mypow(maxn,p - 2);
            for (int i = 0;i < maxn;i++)
                a[i] = 1ll * a[i] * inv % p;
        }
    }
    int tmp[N + 5];
    void INV(int *a,int *ans,int n)
    {
        if (n == 1)
        {
            ans[0] = mypow(a[0],p - 2);
            return;
        }
        INV(a,ans,n + 1 >> 1);
        pre(n * 2);
        for (int i = 0;i < n;i++)
            tmp[i] = a[i];
        clear(tmp,maxn,n);
        ntt(tmp,1);
        ntt(ans,1);
        for (int i = 0;i < maxn;i++)
            ans[i] = (2ll * ans[i] % p - 1ll * tmp[i] * ans[i] % p * ans[i] % p) % p;
        ntt(ans,0);
        clear(ans,maxn,n);
    }
    int main()
    {
        scanf("%d",&n);
        pre(n * 2 + 2);
        for (int i = 1;i < maxn;i <<= 1)
        {
            int g1 = mypow(3,(p - 1) / (i << 1)),g2 = mypow(mypow(3,p - 2),(p - 1) / (i << 1));
            G[i][0] = G[i][1] = 1;
            for (int j = 1;j < i;j++)
                G[i + j][1] = 1ll * G[i + j - 1][1] * g1 % p,G[i + j][0] = 1ll * G[i + j - 1][0] * g2 % p;
        }
        a[0] = fac[0] = 1;
        for (int i = 1;i <= n;i++)
            fac[i] = 1ll * fac[i - 1] * i % p;
        for (int i = 1;i <= n;i++)
            a[i] = 1ll * mypow(2,1ll * i * (i - 1) / 2) * mypow(fac[i],p - 2) % p;
        INV(a,ans,n + 1);
        for (int i = 1;i <= n;i++)
            ans[i] = 1ll * (p - ans[i]) % p * fac[i] % p;
        for (int i = 1;i <= n;i++)
        {
            if (i == 1)
                printf("1
    ");
            else
                if (i == 2)
                    printf("-1
    ");
                else
                {
                    ans[i] = 1ll * fac[i - 1] * mypow(2,1ll * i * (i - 1) / 2 - i) % p * mypow(ans[i],p - 2) % p;
                    printf("%d
    ",(ans[i] + p) % p);
                }
        }
        return 0;
    }
    
  • 相关阅读:
    c# 扩展方法奇思妙用高级篇八:Type类扩展
    Asp.Net 上传大文件专题
    波形捕捉:(2)创建捕捉设备对象
    Capturing Waveforms【译】
    波形捕捉:(1)枚举"捕捉设备"
    C# 调用sql 2000存储过程
    HTTP请求流程(一)流程简介
    Asp.Net 上传大文件专题(4)利用ajax技术显示上传进度
    c# GDI+简单绘图(四)
    波形捕捉:(5)“捕捉缓冲区”信息
  • 原文地址:https://www.cnblogs.com/sdlang/p/13617096.html
Copyright © 2011-2022 走看看