随机立方体
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)个位置的数,方案数为(prodlimits_{j=0}^{i-1}frac{(a_{j+1}-1)!}{a_j!})。
因此总方案数为(n^{underline i}m^{underline i}l^{underline i}V!prodlimits_{j=1}^ifrac1{a_i})。
因为(f_i)是概率,所以还要除掉总方案数(V!),即(f_i=n^{underline i}m^{underline i}l^{underline i}prodlimits_{j=1}^ifrac1{a_i})。
(prodlimits_{j=1}^ifrac1{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
",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)=sumlimits f_{u,i}x^i)。
对于边(u
ightarrow v),我们有转移(F_u(x)leftarrow F_u(x)F_v(x))。
对于边(uleftarrow 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);
}