题目大意
给定一个
n×m
的矩阵
A
,所有元素都是在区间
我们称点
(i,j)
为鞍点,当且仅当
Ai,j
是第
i
行和第
答案对
l
取模。
题目分析
我们令
fi,j
表示我们放置了至少
i
个鞍点(这
考虑如何转移。由于放置相等的鞍点限定了这些位置不能同行/同列,所以我们得将这些鞍点同时转移,否则会出错。
枚举下一步会放置
综上,
fi,jCxn−iCxm−ix!jx(n−i)+x(m−i)+x2−x
可以转移到
fi+x,j+1
。注意由于定义,
x
可以为
使用容斥原理统计答案:
为什么要乘上后面的乘幂呢?因为但是计算 fi,k 显然会有一些没有被考虑过的位置剩余,这些位置总数显然为 (n−i)(m−i) ,填什么都没关系。
还有就是记得设置 00 为 1 ,因为在
时间复杂度
代码实现
#include <iostream>
#include <cstdio>
using namespace std;
const int N=2005;
const int K=11;
int C[N][N],POW[K][N*N],fact[N];
int f[N*N][K];
int n,m,mi,mx,k,P,ans;
void pre()
{
mi=min(n,m),mx=max(n,m);
C[0][0]=1;
for (int i=1;i<=mx;i++)
{
C[i][0]=1;
for (int j=1;j<=i;j++) C[i][j]=(C[i-1][j-1]+C[i-1][j])%P;
}
fact[0]=1;
for (int i=1;i<=mi;i++) fact[i]=1ll*fact[i-1]*i%P;
POW[0][0]=1;
for (int i=1;i<=k;i++)
{
POW[i][0]=1;
for (int j=1;j<=n*m;j++) POW[i][j]=1ll*POW[i][j-1]*i%P;
}
}
void dp()
{
f[0][0]=1;
for (int i=0;i<=mi;i++)
for (int j=0;j<k;j++)
if (f[i][j])
for (int x=0;x<=mi-i;x++)
(f[i+x][j+1]+=1ll*fact[x]*f[i][j]%P*C[n-i][x]%P*C[m-i][x]%P*POW[j][(n-i)*x+(m-i)*x-x*x-x]%P)%=P;
ans=0;
for (int i=1;i<=mi;i++)
{
int sign=(i&1)?1:-1;
(((ans+=1ll*sign*f[i][k]*POW[k][(n-i)*(m-i)]%P)%=P)+=P)%=P;
}
}
int main()
{
freopen("sad.in","r",stdin),freopen("sad.out","w",stdout);
scanf("%d%d%d%d",&n,&m,&k,&P);
pre(),dp();
printf("%d\n",ans);
fclose(stdin),fclose(stdout);
return 0;
}