COCI Mobitel
COCI Mobitel
DP状态设计还是非常妙的,记录一下。朴素的DP比较容易想,设\(F[i][j][k]\)表示从(1,1)走到(i,j)乘积至少为k的路径条数,根据状态转移第一维可以滚动优化掉,但是复杂度\(O(rsn)\)我们无法接受。而且你发现这样转移状态更新貌似非常麻烦,假设是乘积是x,你需要把[1,x]全部更新一下。并且许多数好像都不太重要,不更新不行,更新了对最后的答案没有贡献。
考虑优化,前两维是优化不了的,考虑优化最后一维,变一下状态表示最后一维k代表还要\(\times\)k才能到n。这样子根据整除的性质,最后有用是数字是不会超过\(\sqrt n\)的,整除分块。一段数值x的\(\frac{n}{x}\)值是相同的。复杂度就变成了\(O(rs\sqrt n)\)。提前预处理出\(\frac{n}{[1,n]}\)上取整的值。然后DP即可。注意数组大小要开够。
signed main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
cin >> r >> s >> n;
for(int i = 1;i <= r;i++)
for(int j = 1;j <= s;j++) cin >> g[i][j];
for(int i = 1;i <= n;i++) inv[i] = calc(n,i);
for(int i = 1;i <= n;i++)
if(inv[tot] != inv[i]){
inv[++tot] = inv[i];
block[inv[i]] = tot;/*离散化*/
}/*构成映射关系*/
f[1][1][block[calc(n,g[1][1])]] = 1;
for(int i = 1;i <= r;i++)
for(int j = 1;j <= s;j++)
for(int k = 1;k <= tot;k++){/*还需要乘calc()*/
if(i != r){
int& dw = f[(i + 1) & 1][j][block[calc(inv[k],g[i + 1][j])]];
dw = (dw % mod + f[i & 1][j][k] % mod) % mod;
}
if(j != s){
int& rt = f[i & 1][j + 1][block[calc(inv[k],g[i][j + 1])]];
rt = (rt % mod + f[i & 1][j][k] % mod) % mod;
} /*只留下右下角*/
if(i != r || j != s || k != tot) f[i & 1][j][k] = 0;/*转移后清空,一般滚动数组不需要?*/
}
cout << f[r & 1][s][tot] % mod << endl;
return 0;
}
艰难困苦,玉汝于成

浙公网安备 33010602011771号