题目大意
一个n*m矩形,每个格子的数都在[1,p],求至少有一个鞍点的矩形个数。(i,j)为鞍点定义为(i,j)的权值在第i行和第j列都严格最大。
DP
我们设f[i,j]表示目前有i个鞍点,鞍点的权值不大于j的矩形个数。
我们把这i行i列摆在一起。
现在枚举权值为j+1的鞍点个数x,那么就是从f[i,j]推到f[i+x,j+1],选x行选x列的方案Cxn−i∗Cxm−i,将它们任意排列还有x!的方案,然后鞍点所在行列其余部分都只能放<=j,方案为jx∗(n−i)+x∗(m−i)−x∗x−x
容斥原理
做完dp后,求答案可以容斥。
因为可以把有i个鞍点看做至少有i个。
所以ans=∑min(n,m)i=1(−1)i−1∗f[i,p]∗p(n−i)∗(m−i)
注意计算过程中所有求幂的我们都可以预处理来消掉log。
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=2000+10;
int f[maxn][20],c[maxn][maxn],fac[maxn],mi[20][maxn*maxn];
int i,j,k,l,t,x,n,m,p,mo,ans;
int main(){
scanf("%d%d%d%d",&n,&m,&p,&mo);
if (n>m) swap(n,m);
c[0][0]=1;
fo(i,1,m){
c[i][0]=1;
fo(j,1,m) c[i][j]=(c[i-1][j]+c[i-1][j-1])%mo;
}
fo(i,1,p){
mi[i][0]=1;
fo(j,1,n*m) mi[i][j]=(ll)mi[i][j-1]*i%mo;
}
mi[0][0]=1;
fac[0]=1;
fo(i,1,m) fac[i]=(ll)fac[i-1]*i%mo;
f[0][0]=1;
fo(i,0,n)
fo(j,0,p-1)
fo(x,0,n-i)
(f[i+x][j+1]+=(ll)f[i][j]*c[n-i][x]%mo*c[m-i][x]%mo*fac[x]%mo*mi[j][x*(n-i)+x*(m-i)-x*x-x]%mo)%=mo;
fo(i,1,n){
t=(ll)f[i][p]*mi[p][(n-i)*(m-i)]%mo;
if (i%2==0) (ans-=t)%=mo;else (ans+=t)%=mo;
}
(ans+=mo)%=mo;
printf("%d\n",ans);
}