传送门
解析:
请先确保你看懂了OJ上的提示的暴力做法再来看这个题解。
既然你已经会暴力了,那进入正题。
实际上我们就没有必要计算每个矩阵并求和,我们只需要枚举每个
i
i
i并计算
m
i
n
{
B
i
}
=
i
min\{B_i\}=i
min{Bi}=i的矩阵有多少个就行了。
现在考虑令矩阵一行的和为 i i i的方案数 f i f_i fi,显然 f i = C m i × x m − i × y i f_i=C_m^i\times x^{m-i}\times y^{i} fi=Cmi×xm−i×yi。
那么当一个矩阵 m i n { B j } = i min\{B_j\}=i min{Bj}=i的时候,必然每一行每一列的 B j B_j Bj都 ≥ i \geq i ≥i,那么对于一行来说, B j ≥ i B_j\geq i Bj≥i的概率就是 f j f_j fj的后缀和。
那么这道题可以 O ( m l o g n ) O(mlogn) O(mlogn)做完,因为计算概率是每行都要计算,需要一个快速幂。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const
cs int mod=1000000007;
inline int quickpow(int a,int b,int res=1){
for(;b;b>>=1,a=1ll*a*a%mod)if(b&1)res=1ll*res*a%mod;
return res;
}
cs int N=200005;
int n,m,x,y,suf,ans;
int fac[N],inv[N],ifac[N];
inline int C(cs int &n,cs int &m){
return 1ll*fac[n]*ifac[m]%mod*ifac[n-m]%mod;
}
signed main(){
scanf("%d%d%d%d",&n,&m,&x,&y);
fac[0]=fac[1]=inv[0]=inv[1]=ifac[0]=ifac[1]=1;
for(int re i=2;i<=m;++i){
fac[i]=1ll*fac[i-1]*i%mod;
inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
ifac[i]=1ll*ifac[i-1]*inv[i]%mod;
}
for(int re i=m;i;--i){
suf=(suf+1ll*C(m,i)*quickpow(x,m-i)%mod*quickpow(y,i)%mod)%mod;
ans=(1ll*ans+quickpow(suf,n))%mod;
}
cout<<ans<<endl;
return 0;
}