• dtoj#4224. 小L的占卜


    题目描述:

     小 X 的姐姐小 F 是一名 X​ 国的占星师,她平日的工作就是为 X 国进行占星。 根据 X 国的观测,一个星座可以被描述为一个由 $N​$ 个星体、$N−1​$ 条星路组成的 结构,星体按照发现顺序依次被标号为 $1,2,3,...,N​$ ,一条星路连接了两个不同的星体 $x,y​$ 。此外,X 国的研究还发现,一个星座中的任意两个星体都可以沿着星路互相到达。 每当占星之时,小 F 会对指定的星座进行观测。由于星座的不稳定性,在每次观 测时,星座中全部的  $C_{n+1}^{2}​$ 条简单路径中会有恰好一条亮起,每一条路径亮起的概率 是相等的。如果每一个亮起的星体在之前的观测中都没有亮起过,那么小 F 会记录下 这次观测中亮起的星体,并重新进行一次观测;否则,小 F 会终止本次占星。 显然,这个过程是一定会停下来的,因此小 F 希望你能够告诉她她进行观测的期 望次数。可以证明,这个期望次数一定是一个有理数  $frac{P}{Q}​$ ,你只需要告诉小 F 这个数在 模 $998244353​$ 意义下的余数 $P imes Q^{-1}​$ 即可。 

    输入:

    第一行一个整数 $Num$ ,表示测试点编号,以便选手方便地获得部分分,你可能不 需要用到这则信息,样例中 $Num$  的含义为数据范围与某个测试点相同。 接下来一行一个整数 $N$ ,表示星座中星体的数量。 接下来 $ N−1$  行,每行两个整数 $x,y$ ,表示一条连接 $x,y$  的星路。 

    数据范围:

    对于所有测试数据,保证 $1≤N≤5000$ ,输入的星路图构成一个星座。 

    算法标签:树形dp

    思路:

    考虑计算恰好在第 $i$ 次重复的方案书,答案为第 $i$ 次互不相交的方案数 $ imes $ 路径条数( $C_{n+1}^{2}$ )$-$ 第 $i+1$ 次互不相交的方案数 $ imes (i+1)$ 。

    考虑用树形 $dp$ 维护互不相交的方案数。

    $f[i][j][0/1/2]$ 表示在 $i$ 这个位置上,已经有了$j$ 条互不相交的路径,$0$ 表示我的子树内部已经拥有完整的路径,$1$ 表示我的子树有一条单独的链向上延申,$2$ 表示我的两个子树两条向上延伸的链合并成一条链。
    $$
    h[j+k][0]=h[j+k][0]+g[j][0] imes f[v][k][0]
    $$

    $$
    h[j+k][1]=h[j+k][1]+g[j][0] imes f[v][k][1]+g[j][1] imes f[v][k][0]
    $$

    $$
    h[j+k][2]=h[j+k][2]+g[j][2] imes f[v][k][0]
    $$

    $$
    h[j+k+1][2]=h[j+k+1][2]+g[j][1] imes f[v][k][1]
    $$

     以下代码:

    #include<bits/stdc++.h>
    #define il inline
    #define LL long long
    #define _(d) while(d(isdigit(ch=getchar())))
    using namespace std;
    const int N=5005,p=998244353;
    int n,head[N],ne[N<<1],to[N<<1],cnt,f[N][N][3],g[N][3],h[N][3],sz[N];
    il int read(){
        int x,f=1;char ch;
        _(!)ch=='-'?f=-1:f;x=ch^48;
        _()x=(x<<1)+(x<<3)+(ch^48);
        return f*x;
    }
    il void ins(int x,int y){
        ne[++cnt]=head[x];
        head[x]=cnt;to[cnt]=y;
    }
    il int mu(int x,int y){
        return x+y>=p?x+y-p:x+y;
    }
    il int ksm(LL a,int y){
        LL b=1;
        while(y){
            if(y&1)b=b*a%p;
            a=a*a%p;y>>=1;
        }
        return b;
    }
    il void dfs(int x,int fa){
        for(int i=head[x];i;i=ne[i])if(fa^to[i])dfs(to[i],x);
        for(int i=0;i<=n+1;i++)for(int j=0;j<3;j++)g[i][j]=0;g[0][0]=1;
        for(int i=head[x];i;i=ne[i]){
            if(fa==to[i])continue;
            int v=to[i];
            for(int j=sz[x];j>=0;j--){
                for(int k=sz[v];k>=0;k--){
                    h[j+k][0]=mu(h[j+k][0],1ll*g[j][0]*f[v][k][0]%p);
                    h[j+k][1]=mu(h[j+k][1],mu(1ll*g[j][0]*f[v][k][1]%p,1ll*g[j][1]*f[v][k][0]%p));
                    h[j+k][2]=mu(h[j+k][2],1ll*g[j][2]*f[v][k][0]%p);
                    h[j+k+1][2]=mu(h[j+k+1][2],1ll*g[j][1]*f[v][k][1]%p);
                }
            }
            sz[x]+=sz[v];
            for(int j=0;j<=sz[x];j++)for(int k=0;k<3;k++)g[j][k]=h[j][k],h[j][k]=0;
        }
        for(int i=0;i<=sz[x];i++){
            f[x][i][0]=mu(f[x][i][0],mu(g[i][2],g[i][0]));
            f[x][i+1][0]=mu(g[i][0],g[i][1]);
            f[x][i][1]=mu(g[i][0],g[i][1]);
        }
        sz[x]++;
    }
    int main()
    {
        read();n=read();
        for(int i=1;i<n;i++){
            int x=read(),y=read();
            ins(x,y);ins(y,x);
        }
        dfs(1,0);int s=n*(n+1)/2,ans=0,tmp=ksm(s,p-2);
        for(int i=1,inv=1ll*tmp*tmp%p,fac=1;i<=n;i++,inv=1ll*inv*tmp%p,fac=1ll*fac*i%p){
            int res=mu(1ll*f[1][i][0]*s%p,p-1ll*f[1][i+1][0]*(i+1)%p);
            ans=mu(ans,1ll*res*(i+1)%p*inv%p*fac%p);
        }
        printf("%d
    ",ans);
        return 0;
    }
    View Code
  • 相关阅读:
    delphi 静态3维数组。 严重占用堆栈 切记。 应该用动态数组, 非要用静态数组的话, 要在编译器里 把 堆栈 调大
    Softmax 函数的特点和作用是什么?
    笔记本按开机键没反应怎么办
    小鸡 模拟器 支持双手柄。
    windows7 玩 WinKawaks kof2002为什么提示couldn't initialise DirectSound?
    Delphi中堆栈区别
    最让人纠结的等式:0.999...=1
    求 主板型号 945GME
    电脑可以识别sd卡手机无法识别 的解决方法。 我成功了。 淘宝买的sd卡 不用退货了。 退的人肝疼
    【线段树】HDU 5443 The Water Problem
  • 原文地址:https://www.cnblogs.com/Jessie-/p/10461395.html
Copyright © 2020-2023  润新知