luogu P5307 [COCI2019] Mobitel
题面传送门
显然有\(O(nmw)\)的dp但是不能过。
我们发现对于\([w/2+1,w-1]\)这一段区间内的数\(\times 2\)都是一样的。这启示我们压起来。
根据整除分块原理只有\(O(\sqrt w)\)个不同的数。
所以就可以优化到\(O(nm\sqrt w)\)了。
code:
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<algorithm>
#include<bitset>
#include<map>
#include<set>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define l(x) x<<1
#define r(x) x<<1|1
#define re register
#define ll long long
#define S 1100000
#define N 300
#define K 1000
#define W 1000000
#define mod 1000000007
using namespace std;
int n,nows[K+5<<1],g[W+5],m,w,a[N+5][N+5],head,last,now;ll dp[3][N+5][K+5<<1];
int main(){
freopen("1.in","r",stdin);
re int i,j,k;scanf("%d%d%d",&n,&m,&w);w--;
for(i=w;i;i--) nows[head]^(w/i)&&(nows[++head]=w/i,g[w/i]=head);
for(i=1;i<=n;i++){
for(j=1;j<=m;j++)scanf("%d",&a[i][j]);
}dp[1][1][g[w/a[1][1]]]++;
for(i=1;i<=n;i++){
now=i&1;last=now^1;memset(dp[last],0,sizeof(dp[last]));
for(j=1;j<=m;j++){
for(k=0;k<=head;k++) dp[now][j][k]%=mod,j^m&&(dp[now][j+1][g[nows[k]/a[i][j+1]]]+=dp[now][j][k]),i^n&&(dp[last][j][g[nows[k]/a[i+1][j]]]+=dp[now][j][k]);
}
}
printf("%lld\n",dp[n&1][m][0]);
}