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; }
以上是关于dtoj#4224. 小L的占卜的主要内容,如果未能解决你的问题,请参考以下文章