[CodeForces908G]New Year and Original Order

题意:定义函数 S(x) S ( x ) 是把 x x 各数位排序后得到的数,求i=1nS(i)

S(2018)=128,S(998244353)=233445899 S ( 2018 ) = 128 , S ( 998244353 ) = 233445899

n10700 n ≤ 10 700


看着 n n 这么大显然是数位DP

扒一下官方题解


This is a digit dp d p problem. Let’s try to solve the subproblem “How many ways can the ith i − t h digit be at least j j ?”. Let’s fix j, and solve this with dp d p . We have a dp state dp[a][b][c] d p [ a ] [ b ] [ c ] =number of ways given we’ve considered the first a digits of X X , we need b more occurrences of digits at least j j , and c is a boolean saying whether or not we are strictly less than X X or not yet.

For a fixed digit, we can compute this dp table in O(n2) time, and then compute the answers to our subproblem for each i i (i.e.1 by varying b b in our table).

1.i.e. in other words/this is


考虑到原问题很难做,我们转换一下问题

举个例子:假设第 i i 位填3,那么他的贡献的 310i 3 ∗ 10 i ,考虑把这个贡献拆开

33,32,31 3 ≥ 3 , 3 ≥ 2 , 3 ≥ 1 我们在这一位填 1,2,3 1 , 2 , 3 时都记上 10i 10 i 的贡献

那么我们就只要记这一位填大于等于某个数的方案数就好了

考虑按照套路 dp[i][j][k][l] d p [ i ] [ j ] [ k ] [ l ] 表示前 i i 位有j位的数字大小大于等于 k k ,是否严格小于n的方案数

枚举第 i+1 i + 1 位填 p p

f[i+1][j+(p>=k)][k][l|(p<ai+1)]=f[i][j][k][l]

然后实际上假设前 n n 位我们j位数字大于等于 k k 的方案数是sum=f[n][j][k][0]+f[n][j][k][1]

这个对答案的贡献的是 sum11111j1 s u m ∗ 111 … 11 ⏟ j 个 1

 #include<bits/stdc++.h>
#define fp(i,a,b) for(register int i=a,I=b+1;i<I;++i)
#define fd(i,a,b) for(register int i=a,I=b-1;i>I;--i)
#define go(u) for(register int i=fi[u],v=e[i].to;i;v=e[i=e[i].nx].to)
#define file(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
using namespace std;
const int N=705,P=1e9+7;
int n,ans,a[N],f[N][N][10][2];char s[N];
inline void add(int&a,int b){a+=b,a>=P?a-=P:0;}
int main(){
    #ifndef ONLINE_JUDGE
        file("s");
    #endif
    scanf("%s",s+1);n=strlen(s+1);
    fp(i,1,n)a[i]=s[i]-48;
    fp(i,0,9)f[0][0][i][0]=1;
    fp(i,0,n-1)fp(j,0,i)fp(k,1,9)fp(l,0,1)fp(p,0,(l?9:a[i+1]))
         add(f[i+1][j+(p>=k)][k][l|(p<a[i+1])],f[i][j][k][l]);
    fp(k,1,9){
        int tp=1;
        fp(i,1,n)add(ans,1ll*tp*(f[n][i][k][0]+f[n][i][k][1])%P),tp=(10ll*tp+1)%P;
    }printf("%d",ans);
return 0;
}

发现这个转移是可以滚动的,而且每一个 k k <script type="math/tex" id="MathJax-Element-14892">k</script>还可以分开考虑贡献

所以泥萌可以自己再卡一卡常数

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值