首先可以想到设状态 \(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;
}
标签:原点,题解,LL,uim,long,逃离,getchar
From: https://www.cnblogs.com/zhangyuzhe/p/17739734.html