题解:HDU 7441
1. Description
给定一个长度为 \(k\) 的关系串 \(R\),称一个长度为 \(k+1\) 的非负整数序列满足 \(R\) 的限制,表示对于 \(1\le i\le k\),都有 \(\begin{cases}a_i<a_{i+1}\ (R_i='<')\\a_i>a_{i+1}\ (R_i='>')\end{cases}\)。
我们规定一个非负整数 \(n\) 可以按照数位写为一个整数序列,例如 \(123\) 可以写为 \(1\ 2\ 3\),特殊的 \(0\) 可以写为 \(0\),现在给定 \(l,r\) 和一个关系串 \(R\),询问 \(\sum_{i=l}^r f(i,R)\),其中 \(f(i,R)\) 表示将 \(i\) 写为整数序列后,满足 \(R\) 的限制的子序列数量。
2. Solution
显然,这个巨大的数字让我们不能真的枚举 \(i\),然后求解,但是这个数据范围,显然可以使用数位 DP,我们不妨设计状态 \(p,q,pre,lead,limit\),分别表示现在已经定了前 \(p\) 位,选出的子序列已经匹配了 \(q\) 为关系串,子序列的上一位为 \(pre\),有/没有前导零,是/不是上限。
转移的话,只需要枚举这一位放什么数字,是否选入子序列即可。
需要注意的是,这个时候需要把 \(limit\) 也计入记忆化的范畴,因为基本的数位 DP 时,每一个上限只会遍历一次,而这个时候却会遍历不只一次。
3. Code
/*by qwer6*/
/*略去缺省源和快读快写*/
const int N=505,mod=998244353;
int n,m,T;
int a[N],f[N][N][10][2][2],vis[N][N][10][2][2];
char l[N],r[N],R[N];
int mul(int x,int y){
long long res=1ll*x*y;
return res>=mod?res%mod:res;
}
int add(int x,int y){
x+=y;
return x>=mod?x-mod:x;
}
int sub(int x,int y){
x-=y;
return x<0?x+mod:x;
}
int dfs(int p,int q,int pre,bool lead,bool limit,bool mark){
if(p==n+1){
if(lead)return 0;
if(q<=m)return 0;
return 1;
}
if(vis[p][q][pre][lead][limit]==T)return f[p][q][pre][lead][limit];
int res=0;
for(int i=limit?a[p]:9;i>=0;i--){
res=add(res,dfs(p+1,q,pre,lead&i==0,limit&i==a[p],mark));
if(q<=m&&(lead==0||i>=1)){
if(q==0)res=add(res,dfs(p+1,1,i,0,limit&i==a[p],mark));
else{
if(R[q]=='>'&&pre>i)res=add(res,dfs(p+1,q+1,i,0,limit&i==a[p],mark));
if(R[q]=='<'&&pre<i)res=add(res,dfs(p+1,q+1,i,0,limit&i==a[p],mark));
}
}
}
vis[p][q][pre][lead][limit]=T;
f[p][q][pre][lead][limit]=res;
return res;
}
int solve(char *c,bool flag,bool mark){
T++;
n=strlen(c+1);
for(int i=1;i<=n;i++)a[i]=c[i]^'0';
if(flag){
a[n]--;
for(int i=n;i>1;i--){
if(a[i]<0){
a[i]+=10;
a[i-1]--;
}else break;
}
if(a[1]<=0){
n--;
for(int i=1;i<=n;i++)a[i]=a[i+1];
}
}
return dfs(1,0,0,1,1,mark);
}
signed main(){
int t;
read(t);
while(t--){
scanf("%s%s%s",l+1,r+1,R+1);
m=strlen(R+1);
write(sub(solve(r,0,0),solve(l,1,1))),Nxt;
}
}

浙公网安备 33010602011771号