[题解]P9129 [USACO23FEB] Piling Papers G
P9129 [USACO23FEB] Piling Papers G
不怎么常规的数位 DP。
下文中我们规定一个数的最高位为第 \(1\) 位。
下标和值域的限制都可以差分转成前缀求解。
因此我们需要解决的转化为:对于 \(a\) 的某个前缀,其 \(\le t\) 的方案数是多少?
先考虑只有 \(x\to\overline{xa_i}\) 和 \(x\to x\) 的情况,我们可以直接定义 \(f[i][j][0/1/2]\) 表示从 \(a[1\sim i]\) 中取出 \(j\) 个数字进行操作,使得到的值与 \(t\) 长为 \(j\) 后缀的大小关系为小于/等于/大于的方案数。转移是容易的。
如果加上 \(x\to\overline{a_ix}\) 的情况,前插会使后面的元素全部错位,无法递推。
这启发我们为 \(t\) 的两侧都添加限制,即用 \(f[i][l][r][0/1/2]\) 来表示从 \(a[1\sim i]\) 中取出 \(r-l+1\) 个数字进行操作,使得到的值与 \(t[l\sim r]\) 的大小关系为小于/等于/大于的方案数。
对于 \(f[i][x][y][*]\) 的转移,分三种情况讨论即可:
-
不使用 \(a[i]\):
- \(f[i][x][y][*]\gets f[i-1][x][y][*]\)。
-
\(a[i]\) 前插。
-
若 \(a[i]>t[x]\),则 \(f[i][x][y][2]\gets \sum_k f[i-1][x+1][y][k]\)。
-
若 \(a[i]=t[x]\),则 \(f[i][x][y][*]\gets f[i-1][x+1][y][*]\)。
-
若 \(a[i]<t[x]\),则 \(f[i][x][y][0]\gets \sum_k f[i-1][x+1][y][k]\)。
-
-
\(a[i]\) 后插。
-
\(f[i][x][y][0]\gets f[i-1][x][y-1][0]\)。
-
\(f[i][x][y][sta]\gets f[i-1][x][y-1][1]\)(\(sta\) 表示 \(a[i]\) 与 \(t[y]\) 的大小关系)。
-
\(f[i][x][y][2]\gets f[i-1][x][y-1][2]\)。
-
时间复杂度 \(O(qn\log^2 V)\),其中 \(V\) 与 \(A,B\) 同阶,但实际上我们可以预处理出每个区间的答案,这样优化到了 \(O(n^2\log^2 V+q)\)。
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=305,M=20,P=1e9+7;
int n,A,B,a[N],q,l,r,ansA[N][N],ansB[N][N],t[N],m,f[M][M][3];
inline int get(int a,int b){return a<b?0:a==b?1:2;}
inline void add(int &x,int y){x+=y;if(x>=P) x-=P;}
inline void init(int x){
m=0;
while(x) t[++m]=x%10,x/=10;
reverse(t+1,t+1+m);
}
inline void solve(int x,int ans[N][N]){
init(x);
for(int i=1;i<=n;i++){
memset(f,0,sizeof f);
for(int j=i;j<=n;j++){
for(int x=1;x<=m;x++){
for(int y=m;y>x;y--){//类似01背包的空间优化 需要倒序枚举
if(a[j]>t[x]) for(int k=0;k<3;k++) add(f[x][y][2],f[x+1][y][k]);
else if(a[j]==t[x]) for(int k=0;k<3;k++) add(f[x][y][k],f[x+1][y][k]);
else for(int k=0;k<3;k++) add(f[x][y][0],f[x+1][y][k]);
add(f[x][y][0],f[x][y-1][0]);
add(f[x][y][get(a[j],t[y])],f[x][y-1][1]);
add(f[x][y][2],f[x][y-1][2]);
}
}
for(int x=1;x<=m;x++) add(f[x][x][get(a[j],t[x])],2);//处理边界
for(int k=1;k<=m;k++){
add(ans[i][j],f[k][m][0]);
add(ans[i][j],f[k][m][1]);
if(k^1) add(ans[i][j],f[k][m][2]);
}
}
}
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>A>>B,A--;
for(int i=1;i<=n;i++) cin>>a[i];
solve(A,ansA),solve(B,ansB);
cin>>q;
while(q--){
cin>>l>>r;
cout<<(ansB[l][r]-ansA[l][r]+P)%P<<"\n";
}
return 0;
}
浙公网安备 33010602011771号