题目大意:

定义\(R(x) = 每个数在各数位排序后得到的数\)
例如:\(R(321597) = 123579\)
给定一个\(n<=10^{700}\),求\(\sum _{i=1}^n R(i)\)。答案模上\(10^9+7\)。

思路与解法:

难度比较大的一题。显然是要数位\(DP\)的。
最直白的想法就是:求出\(g[i][j]\)表示数字在\(R(x)\)的第\(j\)位出现的次数。
有了这个我们就可以计算答案了。 但是这个玩意不好\(DP\)处理。
考虑数字\(num\)在\(f(x)\)的第\(j\)位出现的条件:\(R(x)\)中大于等于\(num\)的数字有\(j\)个。
这个东西就可以\(DP\)了:
\(f[i][j][k][0/1]\)表示当前确定了\(i\)位数字,
数位中大于等于\(k\)的数位有\(j\)个,数位限制状态为\(0/1\)的数的个数。
转移比较简单,枚举下一位放什么即可。
然后初值不好处理,所以手玩\(i=1\)的,把\(i=1\)当初值就行了。
处理出了\(f[n][j][num][1]\)后,就比较好算答案了。
.
记录后缀和\(ans[num][j] = \sum_{i=j}^n f[n][i][num][1]\)。
那么\(g[i][j] = ans[num][j] - ans[num+1][j]\),就把\(g[i][j]\)求出来了。
然后用\(g[i][j]\)直接计算答案即可。

实现代码:

注:屏幕较窄的电脑上看 代码可能格式不太好,最好拷贝到编辑器后再看。

#include<bits/stdc++.h>
#define RG register
#define IL inline
#define ll long long
#define _ 705
#define mod 1000000007
using namespace std;

ll Ans,ten; int n , num[ _ ];
ll f[ _ ][ _ ][ 10 ][ 2 ],ans[ _ ][ _ ]; char ch[ _ ];

IL void Add(RG ll &x,RG ll y){ x += y; if(x>=mod)x-=mod; }

int main(){
    scanf("%s",ch); n = strlen( ch );
    for(RG int i = 1; i <= n;i ++)
        num[i] = ch[ n-i ] - '0';
    for(RG int t = 0; t <= 9; t ++){
        if(t > num[1])
            f[1][0][t][0] = t , f[1][0][t][1] = num[1]+1 , f[1][1][t][0] = 10-t , f[1][1][t][1] = 0;
        else
            f[1][0][t][0] = t , f[1][0][t][1] = t , f[1][1][t][0] = 10-t , f[1][1][t][1] = num[1]-t+1;
    }
    for(RG int i = 2; i <= n; i ++){
        for(RG int j = 0; j <= i; j ++)
            for(RG int k = 0; k <= 9; k ++){
                for(RG int t = 0; t <= 9; t ++){
                    if(t >= k){
                        Add( f[i][j][k][0] , f[i-1][j-1][k][0] );
                        if( t < num[i] )
                            Add( f[i][j][k][1] , f[i-1][j-1][k][0] );
                        else if( t == num[i] )
                            Add( f[i][j][k][1] , f[i-1][j-1][k][1] );
                    }
                    else if(t < k){
                        Add( f[i][j][k][0] , f[i-1][j][k][0] );
                        if( t < num[i] )
                            Add( f[i][j][k][1] , f[i-1][j][k][0] );
                        else if( t == num[i] )
                            Add( f[i][j][k][1] , f[i-1][j][k][1] );
                    }
                }
            }
        }
    for(RG int nm = 0; nm <= 9; nm ++)
        for (int i = n ; i >= 1 ; i --)
            (f[n][i][nm][1] += f[n][i+1][nm][1]) %= mod , ans[nm][i]=f[n][i][nm][1];
    Ans = 0; ten = 1;
    for(RG int i = 1; i <= n; ten*=10,ten %= mod,i ++)
        for(RG int nm = 1; nm <= 9; nm ++)
            Add( Ans , ( ((ans[nm][i] - ans[nm+1][i]) % mod + mod) % mod * ten % mod * nm % mod) );
    cout << Ans;  return 0;
}

CF908G Original Order的更多相关文章

  1. 【CF908G】New Year and Original Order(动态规划)

    [CF908G]New Year and Original Order(动态规划) 题面 洛谷 CF 题解 设\(f[i][j][k][0/1]\)表示当前填到了第\(i\)位,有\(j\)个大于等于 ...

  2. 【CF908G】New Year and Original Order 数位DP

    [CF908G]New Year and Original Order 题意:令S(i)表示将i中所有数位上的数拿出来,从小到大排序后组成一个新的数的值.如S(50394)=3459.求$\sum\l ...

  3. 【CF908G】New Year and Original Order

    [CF908G]New Year and Original Order 题面 洛谷 题解 设\(f[i][j][k][l]\)表示当前在第\(i\)位有\(j\)位大于等于\(k\),当前有没有卡上界 ...

  4. Good Bye 2017 G. New Year and Original Order

    G. New Year and Original Order time limit per test 2 seconds memory limit per test 256 megabytes inp ...

  5. CF908G New Year and Original Order 数位DP

    传送门 看到数据范围到\(10^{700}\)毫无疑问数位DP.那么我们最重要的问题是如何有效地维护所有数位排序之后的数的值. 对于某一个数\(x\),设\(f_{x,i} (i \in [1,9]) ...

  6. CF908G New Year and Original Order

    题面 题意翻译 给定$n<=10^{700}$,问$1$到$n$中每个数在各数位排序后得到的数的和.答案$mod\;10^9+7$. 题解 考虑设$f[i][j][k][0/1]$表示前$i$位 ...

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

    又一次降智…… (数位 DP 原来可以写这么短,学到了) 问题可以转化为求数位中 $\ge k$ 的有恰好 $j$ 位的数的个数.设为 $c_{j,k}$. 那么答案就是:(考虑把 $k$ 的贡献拆开 ...

  8. 908G New Year and Original Order

    传送门 分析 代码 #include<iostream> #include<cstdio> #include<cstring> #include<string ...

  9. Codeforces908G. New Year and Original Order

    给n<=10^700,问1到n中每个数在各数位排序后得到的数的和.答案膜1e9+7. 一看就是数位DP啦..然而并没有什么思路.. 可以尝试统计n(i,j)表示数j在第i位的出现次数,知道了这个 ...

随机推荐

  1. vue环境搭建与创建第一个vuejs文件

    我们在前端学习中,学会了HTML.CSS.JS之后一般会选择学习一些框架,比如Jquery.AngularJs等.这个系列的博文是针对于学习Vue.js的同学展开的. 1.如何简单地使用Vue.js ...

  2. qt中创建进程

    进程:1.QProcess 进程类 QProcess *a=new QProcess(); 2.启动void QProcess::start(const QString & command, ...

  3. mongodb window安装配置

    下载mongodb安装包 1. https://www.mongodb.org/dl/win32/x86_64-2008plus-ssl?_ga=2.233271640.711265466.15193 ...

  4. Git hook实现自动部署

    Git Hook 是 Git 提供的一个钩子,能被特定的事件触发后调用.其实,更通俗的讲,当你设置了 Git Hook 后,只要你的远程仓库收到一次 push 之后,Git Hook 就能帮你执行一次 ...

  5. Android 文件下载三种基本方式

    一.自己封装URLConnection 连接请求类 public void downloadFile1() { try{ //下载路径,如果路径无效了,可换成你的下载路径 String url = & ...

  6. gerrit+nginx+centos安装配置

    安装环境 centos 6.8 gerrit-full-2.5.2.war 下载地址:https://gerrit-releases.storage.googleapis.com/gerrit-ful ...

  7. 1.11 str 字符串

    字符串属于不可变序列,是 文本序列. 字符串的声明 >>> #字符串的声明既可以用单引号也可以用双引号,这两个能方法在效果上是一样的 >>> s = '' > ...

  8. Photoshop CC 常用快捷方法有哪些?

    Photoshop CC 常用快捷方法有哪些? 属性栏 工具栏 控制面板 绘图区 1. 多个图层如何快速居中? 在 控制面板 中选中多个图层创建 链接图层 在 工具栏 选择 移动工具 在 属性栏 点击 ...

  9. 《android开发艺术探索》读书笔记(二)--IPC机制

    接上篇<android开发艺术探索>读书笔记(一) No1: 在android中使用多进程只有一种方法,那就是给四大组件在AndroidMenifest中指定android:process ...

  10. ZOJ - 3261 逆向并查集

    思路:很巧妙的解法.如果按照常规一边读入,一边合并并查集,删边实在没办法做. 首先读入所有的操作,把所有不会被删除的边加入并查集,然后从最后一个操作开始逆向操作,当遇到删边操作,就直接把这条边加入并查 ...