CF1073E Segment Sum

这道数位dp与其他不同的是,这个求的是满足要求的数的和,这种题型的题我们还没有做过。

以前虽然做过一些求和或者求积的题,但都是求每个满足条件的数的数位和二进制1的个数等等的和。而这道题是对\([L,R]\)中满足条件的数直接求和,这意味着基本不会有两个状态得出相同的结果。这种情况我们应该怎么做呢?

总之先打出暴搜,参数中的\(tk\)表示到现在一共有多少个不同的数,\(sum\)表示到现在的和。

1.1 暴搜
#include<bits/stdc++.h>
#define int long long
#define mod 998244353
using namespace std;
int l,r,k,a[30];
int sta[30];
bitset<10> vis;
int dfs(int pos,bool limit,int tk,int sum,bool zero){
if(tk>k) return 0;
if(pos==0) return sum;
int rig=limit?a[pos]:9,ans=0;
for(int i=0;i<=rig;i++){
bool temp=(zero&&i==0);//是否为前导0
int presta=sta[pos];
bool previs=vis[i],add=0;
if(!temp) add=(!vis[i]),sta[pos]=i,vis[i]=1;
ans+=dfs(pos-1,limit&&i==rig,tk+add,(sum*10%mod+i)%mod,temp);
ans%=mod;
sta[pos]=presta,vis[i]=previs;
}
return ans;
}
int solve(int x){
int len=0;
while(x){
a[++len]=x%10;
x/=10;
}
return dfs(len,1,0,0,1);
}
signed main(){
cin>>l>>r>>k;
cout<<solve(r)-solve(l-1);
return 0;
}

接着我们考虑优化,但是却发现怎么也无法入手,为什么?这是因为我们始终是进行到最后再返回\(sum\),而两个状态因为前面填的位都不一样所以\(sum\)几乎不存在一样的,所以没法记忆化。

所以我们考虑另一种优化方式,也就是优化\(sum\)的计算方法,把计算到末尾再返回整个数是多少,改成返回这个数位到最低位的子串是多少。然后在循环里\(ans\)逐个累加,但是返回的值还需要加上自己这个数位啊。所以在逐个累加每一个答案的同时,累加上当前这一位\(sta[pos]*10^{pos}\)。

见下图:

为什么这样就方便记忆化了呢?因为这样每个递归的节点都只关注它后面的值,不会受前面的影响。自然我们就可以确定,如果\(pos\)相同,\(vis\)的值相同,两个状态答案一样。

这样的话暴搜代码就得重制了,见下:

1.2 改进的暴搜
#include<bits/stdc++.h>
#define int long long
#define mod 998244353
using namespace std;
int l,r,k,a[30],ph[30];
int sta[30],f[30][1050];
bitset<10> vis;
pair<int,int> dfs(int pos,bool limit,int tk,bool zero){
if(tk>k) return {0,0};
if(pos==0) return {0,1};
int rig=limit?a[pos]:9;
pair<int,int> ans={0,0};
for(int i=0;i<=rig;i++){
bool temp=(zero&&i==0);//是否为前导0
int presta=sta[pos];
bool previs=vis[i],add=0;
if(!temp) add=(!vis[i]),sta[pos]=i,vis[i]=1;
auto res=dfs(pos-1,limit&&i==rig,tk+add,temp);
ans.first=(ans.first+res.first)%mod;
ans.first+=(i*res.second%mod*ph[pos]%mod);
ans.first%=mod;
ans.second+=res.second;
sta[pos]=presta,vis[i]=previs;
}
return ans;
}
int solve(int x){
int len=0;
while(x){
a[++len]=x%10;
x/=10;
}
return dfs(len,1,0,1).first;
}
signed main(){
ph[1]=1;
for(int i=2;i<=25;i++) ph[i]=ph[i-1]*10%mod;
cin>>l>>r>>k;
cout<<solve(r)-solve(l-1);
return 0;
}

注意到我们废弃了\(sum\)参数,这是因为我们已经修改了\(sum\)的计算方法,改为存在返回值里。但返回值使用了pair<int,int>,这是为什么呢?

  • first表示到目前的\(sum\),即满足条件的数的和。
  • second表示到目前满足条件的数的个数。

为什么还要记个数呢?是因为我们需要知道这个子节点有多少个答案,也就是需要额外加多少个\(sta[pos]*10^{pos}\)。

根据上面我们的推导,用\(f[pos][vis]\)来记忆化即可。大小\(20*1024\)。

(这个题如果再加一问,询问一共有多少个,输出根节点的second即可)

1.3 记忆化
#include<bits/stdc++.h>
#define int long long
#define mod 998244353
using namespace std;
int l,r,k,a[30],ph[30];
int sta[30];
pair<int,int> f[30][1050];
bitset<10> vis;
pair<int,int> dfs(int pos,bool limit,int tk,bool zero){
if(tk>k) return {0,0};
if(pos==0) return {0,1};
int visnum=vis.to_ulong();
if(!limit&&!zero&&f[pos][visnum].first!=-1){
return f[pos][visnum];
}
int rig=limit?a[pos]:9;
pair<int,int> ans={0,0};
for(int i=0;i<=rig;i++){
bool temp=(zero&&i==0);
int presta=sta[pos];
bool previs=vis[i],add=0;
if(!temp) add=(!vis[i]),sta[pos]=i,vis[i]=1;
auto res=dfs(pos-1,limit&&i==rig,tk+add,temp);
ans.first=(ans.first+res.first)%mod;
ans.first+=(i*res.second%mod*ph[pos]%mod);
ans.first%=mod;
ans.second=(ans.second+res.second)%mod;
sta[pos]=presta,vis[i]=previs;
}
if(!limit&&!zero) f[pos][visnum]=ans;
return ans;
}
int solve(int x){
int len=0;
while(x){
a[++len]=x%10;
x/=10;
}
return dfs(len,1,0,1).first;
}
signed main(){
memset(f,-1,sizeof f);
ph[1]=1;
for(int i=2;i<=25;i++) ph[i]=ph[i-1]*10%mod;
cin>>l>>r>>k;
cout<<(solve(r)-solve(l-1)+mod)%mod;
return 0;
}

代码中的bitset<10>可以直接压成一个整数,这样应该会更快。

[题解]CF1073E Segment Sum的更多相关文章

  1. CF1073E Segment Sum 解题报告

    CF1073E Segment Sum 题意翻译 给定\(K,L,R\),求\(L~R\)之间最多不包含超过\(K\)个数码的数的和. \(K\le 10,L,R\le 10^{18}\) 数位dp ...

  2. CF1073E Segment Sum 自闭了

    CF1073E Segment Sum 题意翻译 给定\(K,L,R\),求\(L\)~\(R\)之间最多不包含超过\(K\)个数码的数的和. \(K<=10,L,R<=1e18\) 我 ...

  3. CF1073E Segment Sum

    数位DP,求[L,R]区间内所有"数字内包含的不同数码不超过k个的数字"之和.在状态上加一维状态压缩表示含有的数码集合.一开始读错题以为是求数字的个数.读对题之后调了一会儿. #i ...

  4. Codeforces 1073 E - Segment Sum

    E - Segment Sum 思路: 数位dp 我们平时做的数位dp都是求满足条件的数的个数, 这里要求满足条件的数的和 只要在原来的基础上求每一位的贡献就可以了,所以传参数时要传两个 代码: #p ...

  5. Educational Codeforces Round 53 E. Segment Sum(数位DP)

    Educational Codeforces Round 53 E. Segment Sum 题意: 问[L,R]区间内有多少个数满足:其由不超过k种数字构成. 思路: 数位DP裸题,也比较好想.由于 ...

  6. CodeForces - 1073E :Segment Sum (数位DP)

    You are given two integers l l and r r (l≤r l≤r ). Your task is to calculate the sum of numbers from ...

  7. [LeetCode 题解]:Path Sum

    前言   [LeetCode 题解]系列传送门:  http://www.cnblogs.com/double-win/category/573499.html   1.题目描述 Given a bi ...

  8. LeetCode题解39.Combination Sum

    39. Combination Sum Given a set of candidate numbers (C) (without duplicates) and a target number (T ...

  9. E. Segment Sum(数位dp)

    题意:求一个区间内满足所有数位不同数字个数小于K的数字总和.比如:k=2   1,2,3所有数位的不同数字的个数为1满足,但是123数位上有三个不同的数字,即123不满足. 我们可以使用一个二进制的数 ...

  10. Educational Codeforces Round 53 (Rated for Div. 2) E. Segment Sum

    https://codeforces.com/contest/1073/problem/E 题意 求出l到r之间的符合要求的数之和,结果取模998244353 要求:组成数的数位所用的数字种类不超过k ...

随机推荐

  1. MyBatis-Plus实现部分字段存取加解密

    前言 网上教程大致有两种 1.基于MyBatis-Plus自定义类型处理器(TypeHandler)的方法 2.基于MyBatis的方法(拦截器) 这里使用的第二种,为了保护隐私,这里把package ...

  2. 小白也行的:Easy OCR做一款免费的图片转文字

    \(\text{EasyOCR}\) 支持超过 \(80\) 种语言的识别,包括英语.中文(简繁).阿拉伯文.日文等,并且该库在不断更新中,未来会支持更多的语言.像市面上所有的图片识别都用的是 \(\ ...

  3. 想要精准营销,从学习搭建一套对的标签体系开始丨DTVision分析洞察篇

    在人与人打交道的过程中,我们会在有意无意间给周围的人通过贴标签的方式进行大致的判断,比如好说话的.难相处的.聪明的.爱热闹的--贴标签就是用最快的速度将人和事归类,这是人类运用"模式识别&q ...

  4. AI应用实战课学习总结(6)分类算法分析实战

    大家好,我是Edison. 最近入坑黄佳老师的<AI应用实战课>,记录下我的学习之旅,也算是总结回顾. 今天是我们的第6站,一起了解下分类算法基本概念 以及 通过分类算法辅助疾病诊断的案例 ...

  5. Kong入门学习实践(6)HTTPS与TCP流代理

    最近在学习Kong网关,因此根据老习惯,我会将我的学习过程记录下来,一来体系化整理,二来作为笔记供将来翻看.由于我司会直接使用Kong企业版,学习过程中我会使用Kong开源版. 本篇,我们学习快速配置 ...

  6. 7.Java Spring框架源码分析-IOC-创建spring容器

    目录 1. 要分析的代码 2. 创建ApplicationContext 2.1. AnnotationConfigApplicationContext构造方法 2.2. 刷新ioc容器 2.2.1. ...

  7. pm2启动nextjs项目

    安装pm2 npm install -g pm2 yarn global add pm2 pm2启动项目 npm run xx,就可以写成:pm2 start npm -- run xx pm2 st ...

  8. 前端开发系列084-Node篇之fs

    本文对Node的内置模块fs进行介绍,包括模块的基本情况和简单使用. 一.模块介绍 fs文件模块(File System)是Node的内置核心模块之一,代码中可以通过var fs = require( ...

  9. 1010acm 有疑问

    1010 === 不知道为什么一直超时我心碎了曹操草,有大神给看看么?? #include<cstdlib> #include<cstdio> #include<stri ...

  10. c++ stl iterator erase

    简介 就是C++ 标准库程序删除元素. 其实很简单,只是做个记录 it = monkeys.erase(it); //删除元素后,迭代器失效, //要重新让迭代器指向被删元素的后面