CF908G New Year and Original Order(DP,数位 DP)

又一次降智……

(数位 DP 原来可以写这么短,学到了)

问题可以转化为求数位中 $\ge k$ 的有恰好 $j$ 位的数的个数。设为 $c_{j,k}$。

那么答案就是:(考虑把 $k$ 的贡献拆开,比如 $9$ 的贡献拆成 $1$ 的贡献的 $9$ 倍,然后分配到 $1$ 到 $9$)

$$\sum_{1\le j\le n,1\le k\le 9}c_{j,k}\underbrace{111\dots111}_{j}$$

求 $c_{j,k}$ 可以数位 DP。(此处不是记忆化搜索的形式,所以方程会有点不同)

$f[i][j][k][l]$ 表示前 $i$ 位,$\ge k$ 的有恰好 $j$ 位,$l$ 是有没有顶到上界(就是后面要不要顶着 $n$ 枚举,类似记忆化搜索中的 $limit$)。

转移较为显然,不再赘述。

时间复杂度 $O(len^2)$。(有一个 $100$ 的常数)

#include<bits/stdc++.h>
using namespace std;
const int maxn=777,mod=1e9+7;
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline int read(){
    int x=0,f=0;char ch=getchar();
    while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
    while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
    return f?-x:x;
}
int n,f[maxn][maxn][10][2],ans;
char s[maxn];
int calc(int x){
    int pro=1,s=0;
    FOR(i,1,x){
        s=(s+pro)%mod;
        pro=10ll*pro%mod;
    }
    return s;
}
int main(){
    scanf("%s",s+1);
    n=strlen(s+1);
    FOR(i,0,9) f[0][0][i][0]=1;
    FOR(i,1,n) FOR(j,0,i) FOR(k,0,9) FOR(l,0,9) if(l<k || j){
        if(l==s[i]-'0') f[i][j][k][0]=(f[i][j][k][0]+f[i-1][j-(l>=k)][k][0])%mod;
        f[i][j][k][1]=(f[i][j][k][1]+f[i-1][j-(l>=k)][k][1])%mod;
        if(l<s[i]-'0') f[i][j][k][1]=(f[i][j][k][1]+f[i-1][j-(l>=k)][k][0])%mod;
    }
    FOR(j,0,n) FOR(k,1,9) ans=(ans+1ll*calc(j)*(f[n][j][k][0]+f[n][j][k][1])%mod)%mod;
    printf("%d\n",ans);
}
View Code

 

转载于:https://www.cnblogs.com/1000Suns/p/11215836.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值