题解 小 a 和 uim 之大逃离

题目链接

首先可以想到设状态 \(k_1,k_2\) 表示小 \(a\) 和小 \(uim\) 分别表示他们目前取得的得分,那么最终的答案便是 \(k_1=k_2\) 的时候。

但是这样设置状态的复杂度无疑是高的。并且十分浪费,所以考虑设 \(z\) 表示 \(k_1-k_2\) 的值。那么 \(z=0\) 就是答案。

接着考虑如何处理任意起点出发,根据套路,可以设置一个超级原点,向每个点进行连边,跑 \(dp\) 即可。

\(f_{x,y,z,0/1}\) 表示从超级原点到 \((x,y)\),差值为 \(z\),且现在是 小\(a\)/小\(uim\) 拿分,答案就是 \(\sum_{i=1,j=1}^{n,m} f_{i,j,0,1}\)。将超级原点设为 \((0,0)\) 即可,为了使得第一步是小 \(a\) 取,我们将边界条件设为 \(f_{0,0,0,1}=1\)

\[f_{x,y,z,0}=f_{x-1,y,z-a_{x,y},1}+f_{x,y-1,z-a_{x,y},1}+f_{0,0,z-a_{x,y},1}\\ f_{x,y,z,1}=f_{x-1,y,z+a_{x,y},0}+f_{x,y-1,z+a_{x,y},0}+f_{0,0,z+a_{x,y},0}\\ \]

由于两人得分要 \(\%\;(k+1)\),所以我们将 \(z\) 转化为模意义下的即可。

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
LL read() {
	LL sum=0,flag=1; char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') flag=-1; c=getchar();}
	while(c>='0'&&c<='9') {sum=sum*10+c-'0'; c=getchar();}
	return sum*flag;
}

const LL MOD=1e9+7;
const int N=810;
int n,m,k;
int f[N][N][20][2],a[N][N];

int To(int x) {
	return (x%(k+1)+(k+1))%(k+1);
}

int main() {
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++) {
		for(int j=1;j<=m;j++) {
			a[i][j]=read();
		}
	}
	f[0][0][0][1]=1;
	for(int i=1;i<=n;i++) {
		for(int j=1;j<=m;j++){
			for(int z=0;z<=k;z++) {
				f[i][j][z][1]=((f[i-1][j][To(z+a[i][j])][0]+f[i][j-1][To(z+a[i][j])][0])%MOD+f[0][0][To(z+a[i][j])][0])%MOD;
				f[i][j][z][0]=((f[i-1][j][To(z-a[i][j])][1]+f[i][j-1][To(z-a[i][j])][1])%MOD+f[0][0][To(z-a[i][j])][1])%MOD;
			}
		}
	}
	LL ans=0;
	for(int i=1;i<=n;i++) {
		for(int j=1;j<=m;j++) {
			ans=(ans+f[i][j][0][1])%MOD;
		}
	}
	cout<<ans;
	return 0;
}
posted @ 2023-10-02 10:06  2017BeiJiang  阅读(2)  评论(0编辑  收藏  举报