Loading

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;
}
posted @ 2021-08-29 13:22  场-room  阅读(38)  评论(0)    收藏  举报