CTS2019
随机立方体
Luogu
LOJ
UOJ
首先很显然的是最多存在\(lim=\min(n,m,l)\)个极大数字,记\(V=nml\)。
设\(f_i\)表示有至少\(i\)个极大数字的概率,\(g_i\)表示恰好有\(i\)个极大数字的概率,那么根据二项式反演我们有
选出\(i\)个极大值点的方案数(有顺序)为\(n^{\underline i}m^{\underline i}l^{\underline i}\)。
此时有\(a_i=V-(n-i)(m-i)(l-i)\)个被限制的点。
剩下的\(V-a_i\)个数没有任何限制,我们从\(V\)个数中任选\(V-a_i\)个分配给它们,同时这\(V-a_i\)个数可以自由排列,方案数为\(\frac{V!}{a_i!}\)。
剩下的就是填充\(a_i\)个位置的数,方案数为\(\prod\limits_{j=0}^{i-1}\frac{(a_{j+1}-1)!}{a_j!}\)。
因此总方案数为\(n^{\underline i}m^{\underline i}l^{\underline i}V!\prod\limits_{j=1}^i\frac1{a_i}\)。
因为\(f_i\)是概率,所以还要除掉总方案数\(V!\),即\(f_i=n^{\underline i}m^{\underline i}l^{\underline i}\prod\limits_{j=1}^i\frac1{a_i}\)。
\(\prod\limits_{j=1}^i\frac1{a_i}\)用类似于阶乘逆元的方法计算即可。
#include<cstdio>
#include<algorithm>
using i64=long long;
using i128=__int128;
const int N=5000007,P=998244353;
i64 fac[N],ifac[N],a[N];
int read(){int x;scanf("%d",&x);return x;}
i64 pow(i64 a,int b){i64 r=1;for(;b;b>>=1,a=a*a%P)if(b&1)r=r*a%P;return r;}
int main()
{
fac[0]=1;for(int i=1;i<=5000000;++i) fac[i]=i*fac[i-1]%P;
ifac[5000000]=pow(fac[5000000],P-2);for(int i=5000000;i;--i) ifac[i-1]=i*ifac[i]%P;
for(int T=read();T;--T)
{
int n=read(),m=read(),l=read(),k=read(),lim=std::min({n,m,l});i64 ans=0,inv=1;i128 V=(i128)n*m*l;
for(int i=1;i<=lim;++i) a[i]=(V-(i128)(n-i)*(m-i)*(l-i))%P,inv=inv*a[i]%P;
inv=pow(inv,P-2);i64 t=fac[n]*fac[m]%P*fac[l]%P*ifac[k]%P,z;
for(int i=lim;i>=k;--i) z=t*fac[i]%P*ifac[i-k]%P*ifac[n-i]%P*ifac[m-i]%P*ifac[l-i]%P*inv%P,ans+=(i-k)&1? P-z:z,inv=inv*a[i]%P;
printf("%lld\n",ans%P);
}
}
氪金手游
Luogu
LOJ
UOJ
这个限制大概是外向树套内向树的形式(外向树中的每个点都可能是一棵内向树的根),不妨令\(1\)为根。
设\(f_{u,i}\)表示\(u\)点子树内的\(\sum W=i\)的情况下的合法概率,那么答案就是\(\sum f_{1,i}\),初始时\(f_{u,i}=\frac{ip_{u,i}}{\sum p_{u,j}}\)(我们将\(\frac{W_u}{\sum W_i}\)中的\(W_u\)在此处计算,\(\frac1{\sum W_i}\)在最后处理)。
记\(F_u(x)=\sum\limits f_{u,i}x^i\)。
对于边\(u\rightarrow v\),我们有转移\(F_u(x)\leftarrow F_u(x)F_v(x)\)。
对于边\(u\leftarrow v\),考虑容斥,我们有转移\(F_u(x)\leftarrow F_u(x)(1-F_v(x))\)。
这样转移完之后还需要\(f_{u,i}\leftarrow \frac{f_{u,i}}i\)。
#include<cstdio>
#include<vector>
#include<cstring>
#include<utility>
using i64=long long;
using pi=std::pair<int,int>;
const int N=3007,P=998244353;
int size[N];i64 inv[N],t[N],f[N][N];std::vector<pi>e[N];
int read(){int x;scanf("%d",&x);return x;}
i64 pow(i64 a,int b){i64 r=1;for(;b;b>>=1,a=a*a%P)if(b&1)r=r*a%P;return r;}
void inc(i64&a,i64 b){a+=b-P,a+=a>>63&P;}
void dec(i64&a,i64 b){a-=b,a+=a>>63&P;}
void dfs(int u,int fa)
{
size[u]=1;
for(auto it:e[u])
{
int v=it.first;
if(v==fa) continue;
dfs(v,u),memcpy(t+1,f[u]+1,24*size[u]),memset(f[u]+1,0,24*size[u]);
for(int j=1;j<=3*size[u];++j)
if(t[j])
for(int k=1;k<=3*size[v];++k)
if(f[v][k])
{
i64 x=t[j]*f[v][k]%P;
if(it.second) inc(f[u][j+k],x); else dec(f[u][j+k],x),inc(f[u][j],x);
}
size[u]+=size[v];
}
for(int i=1;i<=3*size[u];++i) f[u][i]=inv[i]*f[u][i]%P;
}
int main()
{
int n=read();i64 ans=0;inv[0]=inv[1]=1;
for(int i=2;i<=3*n;++i) inv[i]=(P-P/i)*inv[P%i]%P;
for(int i=1,a,b,c,d;i<=n;++i) a=read(),b=read(),c=read(),d=pow(a+b+c,P-2),f[i][1]=1ll*a*d%P,f[i][2]=2ll*b*d%P,f[i][3]=3ll*c*d%P;
for(int i=1,u,v;i<n;++i) u=read(),v=read(),e[u].emplace_back(v,1),e[v].emplace_back(u,0);
dfs(1,0);
for(int i=1;i<=3*n;++i) inc(ans,f[1][i]);
printf("%lld",ans);
}