https://www.luogu.org/problemnew/show/P1373

设f(l,i,j):以(i,j)为左上角(起点),小a比小uim多l的方案数

理解:假如在一个点(i,j)小a吸收了x,小uim在他相邻位置吸收了y,即小a比小uim多吸收x-y,则(i,j)为起点最后小a小uim相等的方案数转化为:以小uim再走一步的位置为起点,并且最后小a比小uim多吸收y-x的方案数(全都在模k下)。

状态转移方程见代码解释。

求的是方案数,状态不合法方案数为0就行了,不需要设置-1。

注意一开始k++,在模k下。

#include<bits/stdc++.h>
using namespace std;
#define mod 1000000007
int n,m,k,a[1000][1000],f[20][1000][1000],ans;

int main()
{
//	freopen("input.in","r",stdin);
	int num;
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)cin>>a[i][j];
	k++;
	for(int i=n;i>=1;i--)
	{
		for(int j=m;j>=1;j--)
		{
			for(int l=0;l<k;l++)
			{
				if(j!=m)   //最右一列不能继续向右 
				{
					num=(a[i][j]-a[i][j+1]+k)%k;   //起点比右点多了num 
					f[l][i][j]=(f[(k+(l-num))%k][i][j+2]+f[(k+(l-num))%k][i+1][j+1])%mod;//小uim再走一步 
					if(num==l)f[l][i][j]++;   //这两个点可以单独形成一个组合 
				}
				if(i!=n)   //最下一行不能继续向下 
				{
					num=(a[i][j]-a[i+1][j]+k)%k;    //起点比下点多了num
					f[l][i][j]+=(f[(k+(l-num))%k][i+2][j]+f[(k+(l-num))%k][i+1][j+1])%mod;//小uim再走一步 
					if(num==l)f[l][i][j]++;   //这两个点可以单独形成一个组合
					f[l][i][j]%=mod;
				}
			}
		}
	}
	for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)ans=(ans+f[0][i][j])%mod;
	cout<<ans<<endl;
	//for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)printf("f[0][%d][%d]=%d\n",i,j,f[0][i][j]);
	return 0;
}