Description
Data Constraint
Solution
我们设f[i][j]表示当前有i个格子恰好放了j种颜色的方案数,那么
f[i][j]=f[i−1][j−1]∗(p−(j−1))+f[i−1][j]∗j
。我们设g[j]表示n个格子恰好放了j种颜色的方案数,那么g[j]=f[n][j]。对于假如上一列放了j种颜色,这一列放k种颜色,两列颜色的并集为x,那么造成的方案数为
f1[j][k]=g[j]Cjp∗∑min(j+k,p)x=max(q,j,k)Cj+k−xj∗Cx−jp−j
。
解释一下:我们刚开始讲的g[j]包含的是p中任意j种颜色以任意顺序放入n个格子的方案数,那么对于p中任意j种颜色的方案数都是相等的,即
g[j]Cjp
。明显对于j,k,它们的并集x最大是j+k,但由于最多只有p种颜色,所以x的上限为
min(j+k,p)
,同时,x必须必j和k都大,由于题目要求交集必须大于q,所以x的下限为
max(j,k,q)
。现在要求出k的颜色的组成,为了不重复不遗漏,我们对是否在j和k的交集的颜色分别求
Cj+k−xj
表示j和k的交集有多少种情况(即在j中选出j和k的交集)。
Cx−jp−j
表示在k中但不在j中有多少种情况(因为在k中必在x中,并且从除j外的剩余p-j中颜色中选出)。
现在我们设f2[i][k]表示现在做到第i列,第i列放了j种颜色的方案数,那么
f2[1][k]=g[k],f2[i][k]=∑pj=1f2[i−1][k]∗f1[j][k]
。打一下矩阵乘法即可,时间复杂度为O(
logM∗1003
)。
Code
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const ll maxn=105,mo=998244353;
ll n,i,t,j,k,l,m,p,q,x,f[maxn][maxn],g[maxn],g1[maxn],c1[maxn];
ll ans,d[maxn];
ll mi(ll x,int y){
if (y==1) return x;
if (!y) return 1;
ll t=mi(x,y/2);
if (y%2) return t*t%mo*x%mo;return t*t%mo;
}
ll c(ll x,ll y){
return c1[x]*mi(c1[y]*c1[x-y]%mo,mo-2)%mo;
}
struct code{
ll a[maxn][maxn];
code friend operator * (code x,code y){
code z;memset(z.a,0,sizeof(z.a));int i,j,k;
for (i=1;i<maxn;i++)
for (j=1;j<maxn;j++)
for (k=1;k<maxn;k++)
z.a[i][j]=(z.a[i][j]+x.a[i][k]*y.a[k][j]%mo)%mo;
return z;
}
}f1,f2,b;
void mi1(int y){
while (y>1) d[++d[0]]=y%2,y/=2;
b=f1;
for (i=d[0];i>=1;i--){
b=b*b;
if (d[i]) b=b*f1;
}
}
int main(){
scanf("%d%d%d%d",&n,&m,&p,&q);
c1[0]=1;
for (i=1;i<maxn;i++)
c1[i]=c1[i-1]*i%mo;
f[0][0]=1;
for (i=1;i<=n;i++)
for (j=1;j<=min(i,p);j++)
f[i][j]=(f[i-1][j-1]*(p-(j-1))%mo+f[i-1][j]*j%mo)%mo;
for (j=1;j<=p;j++)
f2.a[1][j]=g[j]=f[n][j],g1[j]=(g[j]*mi(c(p,j),mo-2))%mo;
for (j=1;j<=p;j++)
for (k=1;k<=p;k++){
for (x=max(j,max(k,q));x<=min(j+k,p);x++)
f1.a[j][k]=(f1.a[j][k]+c(j,j+k-x)%mo*c(p-j,x-j)%mo)%mo;
f1.a[j][k]=f1.a[j][k]*g1[k]%mo;
}
if (m>1) mi1(m-1),f2=f2*b;
for (i=1;i<=p;i++)
ans=(ans+f2.a[1][i])%mo;
printf("%lld\n",ans);
}