题解:【CTS2019】P5405 氪金手游
JXR_Kalcium · · 题解
题目大意
给定
接下来你要生成一个长度为
令
数据范围:
解题思路
首先容易发现,若从
一棵有向树不太好处理,所以先考虑特殊情况下如何处理。不妨先考虑外向树(即所有边均为父亲连向儿子)的做法,则此时父亲在排列中的位置必须严格在它的所有儿子之前,问题就转化为:求生成满足所有父亲在排列中的位置均严格在其所有儿子之前的排列的概率。
这个问题的做法是容易的。令一个点
其中无穷级数前的部分表示选择
所以
初值即为
下面考虑加入反向边的情况。通过容斥容易发现,加入一条反向边
AC 代码
#include <bits/stdc++.h>
#define ll long long
#define R register
using namespace std; inline int read() {int x=0,f=1; char c=getchar();
while(c<48 || c>57) {if(c=='-') f=-1; c=getchar();} while(c>47 && c<58)
x=(x<<1)+(x<<3)+c-48, c=getchar(); return x*f;} inline void write(ll x)
{static int st[41]; int tp=0; if(x<0) putchar('-'), x=-x; do st[tp++]=x%10,
x/=10; while(x); while(tp) putchar(st[--tp]+48);} int n,a[1001][3],u,v,
lst1[1001],lst2[1001],cnt1,cnt2,s[1001]; ll f[1001][3001],res[3001],ans;
const int mod=998244353; struct edge {int u,v;} e1[1001],e2[1001];
void add(edge e[], int lst[], int &cnt, int u, int v) {e[++cnt]={lst[u],v};
lst[u]=cnt;} int inv(ll x) {int y=mod-2; ll res=1; while(y)
{if(y&1) res=res*x%mod; x=x*x%mod; y>>=1;} return res;}
void chk(int u, int v, bool t)
{
for(R int j=1; j<=s[u]*3; ++j)
{
for(R int k=1; k<=s[v]*3; ++k)
t?res[j]=(res[j]+f[u][j]*f[v][k])%mod:0,
res[j+k]=(res[j+k]+(t?-1:1)*f[u][j]*f[v][k]%mod+mod)%mod;
}
for(R int j=1; j<=(s[u]+s[v])*3; ++j)
f[u][j]=res[j], res[j]=0; s[u]+=s[v];
}
void dfs(int x, int y)
{
ll t=inv(a[x][0]+a[x][1]+a[x][2]); f[x][1]=a[x][0]*t%mod;
f[x][2]=(a[x][1]*t<<1)%mod; f[x][3]=a[x][2]*t*3%mod; s[x]=1;
for(R int i=lst1[x]; i; i=e1[i].u) {if(e1[i].v!=y) dfs(e1[i].v,x),
chk(x,e1[i].v,0);} for(R int i=lst2[x]; i; i=e2[i].u)
{if(e2[i].v!=y) dfs(e2[i].v,x), chk(x,e2[i].v,1);}
for(R int i=1; i<=s[x]*3; ++i) f[x][i]=f[x][i]*inv(i)%mod;
}
int main()
{
n=read(); for(R int i=1; i<=n; ++i) a[i][0]=read(),
a[i][1]=read(), a[i][2]=read(); for(R int i=1; i<n; ++i)
u=read(), v=read(), add(e1,lst1,cnt1,u,v),
add(e2,lst2,cnt2,v,u); dfs(1,0); for(R int i=1; i<=n*3; ++i)
ans=(ans+f[1][i])%mod; write(ans); return 0;
}