首页 > 其他分享 >P2602 数字计数 题解

P2602 数字计数 题解

时间:2024-01-26 20:44:07浏览次数:32  
标签:int 题解 LL pos 计数 num maxn P2602 dp

Question

P2602 数字计数

求 \([a,b]\) 中的所有整数中,每个数出现的次数

Solution

数位DP板子题

定义 \(dp[i]\) 表示 \(i\) 位数的每种数字有多少个,我们把 \(0\) 和 \(00\) 看成两种不同的数,并且 \(00\) 中算 \(0\) 出现过两次

显然,\(0\sim 9\) 在 \(i\) 位数中出现的次数是一样的,得出递推式

\[dp[i]=dp[i-1]\times 10+10^{i-1} \]

数字 \(x\) 在最高位上的次数为 \(10^{i-1}\) ,在其他位上的次数位 \(dp[i-1]\times 10\)

考虑前缀和,把 \([a,b]\) 拆成 \([0,a-1]\) 和 \([0,b]\),问题转化成求 \([0,x]\) 的各个数出现的次数了

假设当前最高位的数字为 \(num[i]\)

不考虑最高位,每个数字都出现了 \(num[i]\times dp[i-1]\) 次

for(int j=0;j<=9;j++)
    cnt[j] += dp[i-1]*num[i];

最高位的数字: \([0,num[i])\) 出现了 \(10^{i-1}\) 次,\(num[i]\) 出现的次数就是后面的数 \(+1\),例如 \(324\) 中,最高位 \(3\) 出现的次数就是 \(25\)

for(int j=0;j<num[i];j++)
    cnt[j] += ten[i-1];
    LL num2 = 0;
for(int j=i-1;j;j--)
    num2 = num2*10+num[j];
cnt[num[i]] += num2 + 1;

最后排除前导零的影响

cnt[0] -= ten[i-1];

还有一种是记忆化 DFS 的写法,定义都有所不同,定义 \(dp[pos][sum]\) 表示枚举到第 \(pos\) 位,前面有 \(sum\) 个 \(now\)

此时需要特判前导零 \(lead\) ,和前面是否被卡死 \(limit\)

如果前面没有被卡死且没有前导零的情况下,可以使用记忆化

当然,也可以把 \(lead\) 和 \(limit\) 定义到记忆化里面去

Code

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 15;
LL ten[maxn],dp[maxn];
LL cnta[maxn],cntb[maxn];
int num[maxn];

void init(){
    ten[0]=1;
    for(int i=1;i<maxn;i++){
        dp[i] = i*ten[i-1];
        ten[i] = ten[i-1]*10;;
    }
}

void solve(LL x,LL *cnt){
    int len = 0;
    while(x){
        num[++len] = x%10;
        x/=10;
    }
    for(int i=len;i;i--){
        for(int j=0;j<=9;j++)
            cnt[j] += dp[i-1]*num[i];
        for(int j=0;j<num[i];j++)
            cnt[j] += ten[i-1];
        LL num2 = 0;
        for(int j=i-1;j;j--)
            num2 = num2*10+num[j];
        cnt[num[i]] += num2 + 1;
        cnt[0] -= ten[i-1];
    }
}

int main(){
    init();
    LL a,b; cin>>a>>b;
    solve(a-1,cnta),solve(b,cntb);
    for(int i=0;i<=9;i++)
        cout<<cntb[i]-cnta[i]<<" ";
    return 0;
}

记忆化 DFS

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 15;
LL num[maxn],now,dp[maxn][maxn];

LL dfs(int pos,int sum,bool lead,bool limit){
    LL ans = 0;
    if(pos == 0) return sum;
    if( !lead && !limit && dp[pos][sum]!=-1 ) return dp[pos][sum]; //记忆化搜索
    int up = limit ? num[pos] : 9; //这一位的最大值
    for(int i=0;i<=up;i++){
        if( i==0 && lead )
            ans += dfs(pos-1, sum, true, limit && i==up); //计算 000-099
        else if(i==now)
            ans += dfs(pos-1, sum+1, false, limit && i==up); //计算 x00-x99
        else if(i!=now)
            ans += dfs(pos-1, sum, false, limit && i==up); //计算其他
    }
    if(!lead && !limit) dp[pos][sum] = ans; //可以重复使用部分
    return ans;
}

LL solve(LL x){
    int len = 0;
    while(x){
        num[++len] = x%10;
        x/=10;
    }
    memset(dp,-1,sizeof dp);
    return dfs(len,0,true,true);
}

int main(){
    LL a,b; cin>>a>>b;
    for(int i=0;i<10;i++){
        now = i;
        cout<<solve(b)-solve(a-1)<<" ";
    }
    return 0;
}

标签:int,题解,LL,pos,计数,num,maxn,P2602,dp
From: https://www.cnblogs.com/martian148/p/17990663

相关文章

  • 洛谷题解-P2712 摄像头
    https://www.luogu.com.cn/problem/P2712可以看出是拓扑排序,因为是有前后关系的,但是坑点在于点并不连续,值得记录一下(刚开始70分,后来才AC)注意坑点:拓扑排序,遍历的点不连续 #include<bits/stdc++.h>usingnamespacestd;constintN=1e5+5;intn,x,m,y,d[N],cnt=0,v......
  • markdown图床问题解决
    写博客不仅要以文字形式记录,更重要的是把自己曾经的截图记录下来,更方便下次使用。所以有必要搞一个稳定的图床生成图片链接。一开始我是用的Github,新建一个仓库上传图片,优点是方便,缺点是网络不用魔法图片经常加载不出来。后来看到网上一些博主推荐使用七牛云图片存储,为此我购买......
  • CF1654G Snowy Mountain 题解
    题目链接点击打开链接题目解法很牛的题显然可以\(O(n)\)多源\(bfs\)求出\(h_i\)考虑从\(st\)开始最优的操作是什么?先延最短路径到\(p\),然后找到\(p\)的相邻点\(q\),满足\(h_p=h_q\),在\(p,q\)之间横跳,耗完所有动能,然后直接滑下去(不经过高度相同的点)为什么到\(p......
  • [西湖论剑 2022]web部分题解(更新中ing
    [西湖论剑2022]NodeMagicalLogin环境!启动!(ノへ ̄、)这么一看好像弱口令啊,(不过西湖论剑题目怎么会这么简单,当时真的傻),那就bp抓包试一下(这里就不展示了,因为是展示自己思路,这里就写了一下当时的NC思路,其实是不对的┭┮﹏┭┮)不是BP弱口令?那好吧,我们先看一下源码,比赛的时候是给了源......
  • Altair SimSolid常见问题解答 衡祖仿真
    Q:SimSolid究竟有什么特别之处?A:AltairSimSolid是专为设计工程师开发的结构分析软件且非常有创新性。它消除了传统FEA中特别耗时和非常专业的两项庞大任务——几何结构简化和网格划分,是一场仿真变革。简而言之,就是不用做几何简化,不用画网格,复杂装配体数量没有上限,真实三维模型直......
  • 蓝牙BQB认证申请过程常见问题解答
    BQB全名:BluetoothQualificationBody,我们一般称之为蓝牙资格认证,产品具有蓝牙功能并且在产品外观上标明蓝牙标志(Bluetoothlogo),必须通过蓝牙BQB的认证。1、为什么要过BQB?蓝牙技术联盟(BluetoothSpecialInterestGroup,简称SIG),蓝牙技术是它发明的。我们要使用它的专利,必须拿......
  • ARM指针寄存器——堆栈指针寄存器SP、程序计数器PC、连接寄存器LR详解
    堆栈的实现方法        在随机存储器区划出一块区域作为堆栈区,数据可以一个个顺序地存入(压入)到这个区域之中,这个过程称为‘压栈’(push)。通常用一个指针(堆栈指针SP—StackPointer)实现做一次调整,SP总指向最后一个压入堆栈的数据所在的数据单元(栈顶)。从堆......
  • CF1515F Phoenix and Earthquake 题解
    题目链接:CF或者洛谷首先基于一个事实,答案一定是生成树,显然,每次我们都需要连边,每次都会\(-x\),那么一共会减少\((n-1)\timesx\),很显然的一个必要条件为:\[\sum_{i=1}^{n}a_i\ge(n-1)\timesx\显然一定成立。\]现在我们用来证明它同时也是一个充分条件,不妨设:\[a_1\lea......
  • latex常见问题解决
    1.Fileendedwhilescanninguseof\@writefile解决方法:删除编译文件夹内.aux扩展名结尾的文件,重新用Latex命令进行编译,自动生成正确的aux文件,完成错误的修复。注:如果还不好使,就把除.tex以外的文件均删除掉,如:.bbl,.blg,.dvi,.log等2.多行缩进ctrl+a全选后,shift+tab向前退......
  • [Bzoj 3252] 攻略 题解
    攻略题面\(n(\le2\cdot10^5)\)个点的有根树,\(k(\len)\)次从根走到叶子,每个点有权值,求经过的点的权值和的最大值.(同一个点只能算一次)Sol1我们设想一个叶子一个叶子加进去的过程。如果有两个从某个点到叶子的路径,我们可以如图把他分成两条路径。那么他满足贪心,也就是每次......