【洛谷5307】[COCI2019] Mobitel(动态规划)

点此看题面

  • 给定一个(n imes m)的矩阵,从((1,1))走到((n,m)),每次只能向右或向下。
  • 每个格子中有一个正整数,求有多少条路径满足其上所有数乘积大于等于(v)
  • (n,mle300,vle10^6)

暴力动态规划

非常显然的暴力(DP),设(f_{i,j,k})表示走到((i,j)),其中所有数乘积为(k)的方案数(特殊地,若(kge v)统一记作(v))。

转移就只需考虑向下走和向右走两种。

第三维状态的优化

前两维表示当前位置显然不太好优化。

我们考虑第三维,(kge vLeftrightarrow k>v-1Leftrightarrowlfloorfrac{v-1}k floor=0)

而我们又知道整除的运算满足(lfloorfrac{lfloorfrac xy floor}z floor=lfloorfrac x{yz} floor),所以可以直接把(lfloorfrac{v-1}k floor)记作状态,每想给(k)乘上一个数,只要给(lfloorfrac{v-1}k floor)整除以这个数即可。

根据除法分块那套理论,(lfloorfrac{v-1}k floor)的种数只有(2sqrt v)个,足以通过此题。

代码:(O(n^2sqrt v))

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 300
#define V 1000000
#define S 2000
#define X 1000000007
#define Inc(x,y) ((x+=(y))>=X&&(x-=X))
using namespace std;
int n,m,v,a[N+5][N+5],f[2][N+5][S+5],p[V+5],w[S+5];
int main()
{
	RI i,j,k;for(scanf("%d%d%d",&n,&m,&v),i=1;i<=n;++i) for(j=1;j<=m;++j) scanf("%d",&a[i][j]);
	RI t=0;for(i=1;i^v;++i) !p[(v-1)/i]&&(w[p[(v-1)/i]=++t]=(v-1)/i);//记录下所有可能的(v-1) div k
	for(f[1][1][p[(v-1)/a[1][1]]]=i=1;i<=n;++i)
	{
		for(j=1;j<=m;++j) for(k=0;k<=t;++k) f[i&1^1][j][k]=0;//滚存,需事先清空下一行
		for(j=1;j<=m;++j) for(k=0;k<=t;++k)
			i^n&&Inc(f[i&1^1][j][p[w[k]/a[i+1][j]]],f[i&1][j][k]),//向下走
			j^m&&Inc(f[i&1][j+1][p[w[k]/a[i][j+1]]],f[i&1][j][k]);//向右走
	}
	return printf("%d
",f[n&1][m][0]),0;//k≥n等价于(v-1) div k=0
}
败得义无反顾,弱得一无是处
原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu5307.html