小清新区间动态规划
EnofTaiPeople
·
·
题解
Part1 前言
题意:给定 n 个 m 位数 a_i,有 ? 表示该位不确定,我们需要求出将每一个 ? 转换成 0\sim9 使得 a_i<a_{i+1} 的方案数,n,m\le40。
要是 m\le7 就好了!不过怎么可能?
当然如果每个数字的 ? 都不超过 7 也好啊!然而也不可能。
当然这道题还是小清新的。
Part2 设计状态
考虑最高位的分布,首先一定不能存在逆序对。
我们可以简单认为是一段 1,一段 2,一直到一段 9,对于一段内的数字并没有得到严格偏序关系,所以我们需要它们在更低的位中得到严格偏序。
所以有很容易的想到了区间动态规划的形式:设 f_{i,l,r} 表示考虑了第 i 及以后的位数,区间 [l,r] 达成严格偏序的方案数。
边界是 f_{m,l,l}=\begin{cases}1,S_{l,m}\ne?\\10,S_{l,m}=?\end{cases},f_{1,1,n} 是答案。
我们需要从 f_{i+1,l,r} 转移到 f_{i,l,r},分两种情况。
-
区间 [l,r] 在第 i 位完全相等,那么之前就一定要保证严格偏序,从 f_{i+1,l,r} 转移过来,需要考虑出现的非问号数字个数。
-
区间 [l,r] 在第 i 位不完全相等,可以枚举最后一个完全相等区间。
Part3 具体实现
想到这里其实已经不难了,不过状态是真的神秘,这里我设 h_{L,R,l,r} 表示区间 [l,r] 该位为 [L,R] 形成严格偏序的方案数,然后就能 h_{L,R,l,r}\leftarrow h_{L,R',l,k}h_{R,R,k+1,r},其中 R'<R,k 在 [l,r) 之间。
使用了前缀和优化,时间复杂度为 O(c^2mn^3),但是跑不满,所以飞快,空间 O(c^2n^2),其中 c 为数字值域,本题 c=10。
跟同学把这场写完之后,他告诉我这道题其实只需要记该位数字的最大值,但需要再开一维表示只使用某一个数字的答案,这样的时间复杂度为 O(cmn^3),空间 O(cn^2),实际上效率差不多。
```cpp
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
using ld=long double;
using pr=pair<int,int>;
const int N=45,M=998244353;
int n,m,f[N][N],g[N][N],h[11][11][N][N],w;
char s[N][N];
int main(){
scanf("%d%d",&n,&m);
int i,j,k,l,r,x,y,L,R;
for(x=1;x<=n;++x)
scanf("%s",s[x]+1);
for(l=1;l<=n;++l)f[l][l]=1;
for(i=m;i;--i){
memcpy(g,f,sizeof(g));
memset(f,0,sizeof(f));
memset(h,0,sizeof(h));
for(int _k=1;_k<=n;++_k)
for(l=1,r=_k;r<=n;++l,++r){
for(k=l,w=0;k<=r;++k)
if(s[k][i]!='?')w|=1<<(s[k][i]-'0');
if(!w){
for(k=0;k<10;++k)
h[k][k][l][r]=g[l][r];
}else if(w==(w&-w)){
w=__builtin_ctz(w);
h[w][w][l][r]=g[l][r];
}
for(k=l;k<r;++k){
for(L=0;L<10;++L)
for(R=L+1;R<10;++R)
h[L][R][l][r]=(ll(h[L][R-1][l][k])*h[R][R][k+1][r]+h[L][R][l][r])%M;
}
for(L=0;L<10;++L){
for(R=L+1;R<10;++R)
((h[L][R][l][r]+=h[L][R-1][l][r])>=M)&&(h[L][R][l][r]-=M);
((f[l][r]+=h[L][9][l][r])>=M)&&(f[l][r]-=M);
}
}
}printf("%d\n",f[1][n]);
return 0;
}
```