dtoj#4224. 小L的占卜

Posted jessie-

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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

 

以上是关于dtoj#4224. 小L的占卜的主要内容,如果未能解决你的问题,请参考以下文章

题解 DTOJ #1667 小B的询问(query)

dtoj#4259. 越野赛车问题

DTOJ #1772运输任务 / COCI2011OPENRIJEKA

dtoj#4222. 小b爱旅行(travel)

dtoj#4258. 铃铛计数问题

dtoj2612 小星星(star)