2021.10.29 数位dp

1.数字计数

我们先设数字为ABCD

看A000,如果我们要求出它所有数位之和,我们会怎么求?

鉴于我们其实已经求出了0到9,0到99,0到999。。。上所有数字个数(f[i],且没有考虑前导0)我们何不把这个A000看成0000到1000到2000...A000对于不考虑首位每一个式子的数字的出现个数为 \(A*f[3]\)。加上首位出现也就是小于A每一个数都出现了\(10^3\)次,再加上,我们就把A000处理完了。

这样你以为就把第一位处理完了?不不不,首位A还出现了BCD+1次呢,也就是从A000~ABCD,这个A还出现了BCD+1次,所以再加上这些才行呢。那么你发现,我们成功把首位代表的所有数字个数求出来了,剩下的求解与A完全没有任何关系,只是BCD的求解,于是我们发现我们已经把一个大问题,化成了一个个小问题,也即是,对于一个这样n位的数,把他一位位的分离开来。

当然你还需要处理前导0你会发现前导0一定是\(0001,0002\cdots0012,0013\cdots0101,0102\cdots0999\)这样的数,一共出现了\(10*(i-1)+10*(i-2)+\cdots+10\) (i表示数字位数),让0的统计减去这个值,那么恭喜你这道题做完了。

来自[题解 P2602 【ZJOI2010]数字计数】 - moye到碗里来 的博客 - 洛谷博客 (luogu.com.cn)

练习题:

[P2602 ZJOI2010]数字计数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

运用前缀和的想法,把从a到b拆分为从1到b减去从1到a-1的结果

  1. #include<cstdio>
  2. #include<iostream>
  3. #include<algorithm>
  4. #include<cstring>
  5. using namespace std;
  6. const int N=15;
  7. int a,b,f[N],ten[N],numa[N],numb[N],cnt[N];
  8. inline void solve(int x,int *num){
  9. int top=0;
  10. while(x){
  11. cnt[++top]=x%10;
  12. x/=10;
  13. }
  14. for(int i=top;i>=1;i--){
  15. for(int j=0;j<10;j++)num[j]+=f[i-1]*cnt[i];
  16. for(int j=0;j<cnt[i];j++)num[j]+=ten[i-1];
  17. int js=0;
  18. for(int j=i-1;j>=1;j--)js=js*10+cnt[j];
  19. num[cnt[i]]+=js+1;
  20. num[0]-=ten[i-1];
  21. }
  22. }
  23. int main(){
  24. ios::sync_with_stdio(false);
  25. cin>>a>>b;
  26. ten[0]=1;
  27. for(int i=1;i<=12;i++)
  28. f[i]=f[i-1]*10+ten[i-1],ten[i]=ten[i-1]*10;
  29. solve(a-1,numa);solve(b,numb);
  30. for(int i=0;i<10;i++)cout<<numb[i]-numa[i]<<" ";
  31. return 0;
  32. }

PS:数位dp是对每一位上的数字分开进行处理。如数字ABCD,先处理010010002000……A000,然后处理BCD,把BCD拆开处理,先处理0B00,再处理0C0,再处理D,完结撒花

[P2657 SCOI2009] windy 数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

  1. // https://www.luogu.com.cn/discuss/246065
  2. #include<cstdio>
  3. #include<iostream>
  4. #include<algorithm>
  5. #include<cstring>
  6. #include<cmath>
  7. using namespace std;
  8. const int N=15;
  9. int a,b,f[N][N],num[N],len;
  10. inline int calc(int x){
  11. memset(num,0,sizeof(num));
  12. len=0;
  13. while(x){
  14. num[++len]=x%10;
  15. x/=10;
  16. }
  17. int ans=0;
  18. for(int i=1;i<len;i++)
  19. for(int j=1;j<10;j++)//这里不能有前导零,因为是每次枚举不同长度的最高位
  20. ans+=f[i][j];
  21. for(int i=1;i<num[len];i++)ans+=f[len][i];
  22. for(int i=len-1;i>=1;i--){
  23. for(int j=0;j<num[i];j++)//这里可以有前导零,因为这里是枚举在已经有前面固定的几位后剩下长度的为高位,而不是原数的最高位,可以有前导零
  24. if(abs(num[i+1]-j)>=2)ans+=f[i][j];
  25. if(abs(num[i]-num[i+1])<2)break;
  26. }
  27. return ans;
  28. }
  29. int main(){
  30. ios::sync_with_stdio(false);
  31. for(int i=0;i<10;i++)f[1][i]=1;
  32. for(int i=2;i<=10;i++)
  33. for(int j=0;j<10;j++)
  34. for(int k=0;k<10;k++)
  35. if(abs(j-k)>=2)f[i][j]+=f[i-1][k];
  36. cin>>a>>b;
  37. cout<<calc(b+1)-calc(a);
  38. return 0;
  39. }

区间DP

[P2890 USACO07OPEN]Cheapest Palindrome G - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

开始把区间dp写成了求lcs+删字母或加字母,于是10分……其余RE,why?!

10分代码:

  1. #include<cstdio>
  2. #include<iostream>
  3. #include<algorithm>
  4. #include<cstring>
  5. #include<map>
  6. using namespace std;
  7. const int N=2010;
  8. //string s,t;
  9. char s[N],t[N];
  10. int m,n,f[N],id[N][N],fin[N],top,a[N],val[N],minn;
  11. map<int,char>mapi;
  12. inline int find(int x){
  13. int L=1,R=top,mid;
  14. while(L<R){
  15. mid=(L+R)>>1;
  16. if(fin[mid]<=x)L=mid+1;
  17. else R=mid;
  18. }
  19. return L;
  20. }
  21. int main(){
  22. ios::sync_with_stdio(false);
  23. cin>>m>>n;
  24. //cin>>s;
  25. //t=s;reverse(t.begin(),t.end());
  26. scanf("%s",s);
  27. memcpy(t,s,sizeof(s));
  28. reverse(t,t+strlen(t));
  29. //cout<<t<<endl;
  30. for(int i=1;i<=n;i++){
  31. int x=s[i-1]-'a'+1;
  32. ++id[x][0];
  33. id[x][id[x][0]]=i;
  34. //cout<<id[x][id[x][0]]<<" ";
  35. mapi[i]=s[i-1];
  36. }
  37. //cout<<endl;
  38. for(int i=1;i<=m;i++)id[i][0]=0;
  39. for(int i=1;i<=n;i++){
  40. int x=t[i-1]-'a'+1;
  41. ++id[x][0];
  42. a[i]=id[x][id[x][0]];
  43. //cout<<a[i]<<" ";
  44. }
  45. //cout<<endl;
  46. for(int i=1;i<=m;i++)id[i][0]=0;
  47. cout<<" Case 1"<<endl;
  48. fin[1]=a[1];top=1;++id[mapi[a[1]]-'a'+1][0];
  49. cout<<" Case 2"<<endl;
  50. for(int i=2;i<=n;i++)
  51. if(fin[top]<=a[i])fin[++top]=a[i],++id[mapi[a[i]]-'a'+1][0],cout<<i<<endl;
  52. else{
  53. int x=find(a[i]);
  54. cout<<i<<" "<<x<<" "<<mapi[fin[x]]<<endl;
  55. --id[mapi[fin[x]]-'a'+1][0];
  56. fin[x]=a[i];
  57. ++id[mapi[a[i]]-'a'+1][0];
  58. }
  59. for(int i=1;i<=top;i++)cout<<fin[i]<<" ";cout<<endl;
  60. //for(int i=1;i<=top;i++)cout<<mapi[fin[i]]<<" ";cout<<endl;
  61. for(int i=1,j=1;i<=m;i++){
  62. char x;
  63. int u,v;
  64. cin>>x>>u>>v;
  65. int xi=x-'a'+1;
  66. if(mapi[fin[j]]==x)minn+=u*id[xi][0],++j;
  67. val[xi]=min(u,v);//0 add 1 delete
  68. }
  69. //cout<<"minn "<<minn<<endl;
  70. int ans=0;
  71. for(int i=1,j=1;i<=n;){
  72. //cout<<i<<" "<<a[i]<<" "<<j<<" "<<fin[j]<<" "<<ans<<endl;
  73. if(a[i]==fin[j])++i,++j;
  74. else ans+=val[mapi[a[i]]-'a'+1],++i;
  75. }
  76. //cout<<"ans "<<ans<<endl;
  77. cout<<min(minn,ans);
  78. return 0;
  79. }

100分代码:

  1. #include<iostream>
  2. #include<algorithm>
  3. #include<cstring>
  4. #include<map>
  5. using namespace std;
  6. const int N=2010;
  7. int n,m,f[N][N],val[N];
  8. char s[N];
  9. int main(){
  10. //ios::sync_with_stdio(false);
  11. //呵呵了,洛谷上加上这一行直接挂了
  12. cin>>m>>n;
  13. scanf("%s",s+1);
  14. for(int i=1;i<=m;i++){
  15. char x;
  16. int u,v;
  17. cin>>x>>u>>v;
  18. val[x-'a']=min(u,v);
  19. }
  20. memset(f,0x3f3f3f3f,sizeof(f));
  21. for(int i=1;i<=n;i++){
  22. f[i][i]=0;
  23. if(s[i]==s[i+1])f[i][i+1]=0;
  24. }
  25. for(int len=0;len<=n;len++)
  26. for(int i=1;i+len<=n;i++){
  27. int j=i+len;
  28. int u=s[i-1]-'a',v=s[j+1]-'a';
  29. if(u==v)f[i-1][j+1]=min(f[i][j],f[i-1][j+1]);
  30. f[i-1][j]=min(f[i-1][j],f[i][j]+val[u]);
  31. f[i][j+1]=min(f[i][j+1],f[i][j]+val[v]);
  32. }
  33. cout<<f[1][n];
  34. return 0;
  35. }

普通DP

[P1310 NOIP2011 普及组] 表达式的值 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题意:

运算的优先级是:

  1. 先计算括号内的,再计算括号外的。
  2. “× ”运算优先于“⊕”运算,即计算表达式时,先计算× 运算,再计算⊕运算。例如:计算表达式A⊕B × C时,先计算 B × C,其结果再与 A 做⊕运算。

现给定一个未完成的表达式,例如_+(*),请你在横线处填入数字00或者11 ,请问有多少种填法可以使得表达式的值为00。

分析:

思路不会,只会状态转移。

代码如下:

  1. #include<cstdio>
  2. #include<iostream>
  3. #include<algorithm>
  4. #include<cstring>
  5. #include<stack>
  6. using namespace std;
  7. #define int long long
  8. const int N=2e5+10;
  9. const int mod=1e4+7;
  10. int n,m,top=1;
  11. string si;
  12. stack<char>s;
  13. struct node{
  14. int zero,one;
  15. }ans[N];
  16. inline void solve(char ch,node &a,node &b){
  17. if(ch=='+'){
  18. a.one=(a.one*b.one%mod+a.zero*b.one%mod+a.one*b.zero%mod)%mod;
  19. a.zero=a.zero*b.zero%mod;
  20. }else{
  21. a.zero=(a.zero*b.zero%mod+a.one*b.zero%mod+a.zero*b.one%mod)%mod;
  22. a.one=(a.one*b.one)%mod;
  23. }
  24. }
  25. signed main(){
  26. cin>>n;
  27. cin>>si;
  28. si+=')';
  29. //cout<<si<<endl;
  30. ans[1].one=ans[1].zero=1;
  31. s.push('(');
  32. for(int i=0;i<=n;i++){
  33. //cout<<i<<endl;
  34. if(si[i]=='(')s.push('(');
  35. else if(si[i]==')'){
  36. for(;s.top()!='(';s.pop(),top--)
  37. solve(s.top(),ans[top-1],ans[top]);//,cout<<s.top()<<" ";cout<<endl;
  38. s.pop();
  39. }else{
  40. for(;s.top()<=si[i]&&s.top()!='(';s.pop(),top--)
  41. solve(s.top(),ans[top-1],ans[top]);//,cout<<s.top()<<" ";cout<<endl;
  42. s.push(si[i]);
  43. ++top;
  44. ans[top].zero=ans[top].one=1;
  45. }
  46. }
  47. cout<<ans[1].zero;
  48. return 0;
  49. }

2021.10.29 数位dp的更多相关文章

  1. 2021.10.29 P1649 [USACO07OCT]Obstacle Course S(BFS)

    2021.10.29 P1649 [USACO07OCT]Obstacle Course S(BFS) 题意: 给一张n*n的图,起点为A,终点为 B,求从A到B转弯次数最少为多少. 分析: 是否存在 ...

  2. 日常Java 2021/10/29

    Java Object类是所有类的父类,也就是说Java的所有类都继承了Object,子类可以使用Object的所有方法. Object类位于java.lang 包中,编译时会自动导入,我们创建一个类 ...

  3. 数位dp总结

    由简单到稍微难点. 从网上搜了10到数位dp的题目,有几道还是很难想到的,前几道基本都是模板题,供入门用. 点开即可看题解. hdu3555 Bomb hdu3652 B-number hdu2089 ...

  4. HDU3709 Balanced Number (数位dp)

     Balanced Number Time Limit:3000MS     Memory Limit:0KB     64bit IO Format:%lld & %llu Descript ...

  5. bzoj 3209 bzoj1799 数位dp

    3209: 花神的数论题 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2267  Solved: 1040[Submit][Status][Disc ...

  6. 【10.17校内测试】【二进制数位DP】【博弈论/预处理】【玄学(?)DP】

    Solution 几乎是秒想到的水题叻! 异或很容易想到每一位单独做贡献,所以我们需要统计的是区间内每一位上做的贡献,就是统计区间内每一位是1的数的数量. 所以就写数位dp辣!(昨天才做了数字统计不要 ...

  7. 【NOI2019模拟2019.6.29】组合数(Lucas定理、数位dp)

    Description: p<=10且p是质数,n<=7,l,r<=1e18 题解: Lucas定理: \(C_{n}^m=C_{n~mod~p}^{m~mod~p}*C_{n/p} ...

  8. Noip模拟69 2021.10.5

    考场拼命$yy$高精度结果没学好$for$循环痛失$50pts$,当场枯死 以后一定打对拍,要不考后会... T1 石子游戏 首先要知道典型的$NIM$博弈,就是说如果所有堆石子个数的异或和为$0$则 ...

  9. HDU 2089 不要62 (递推+暴力或者数位DP)

    题意:中文题. 析:暴力先从1到1000000,然后输出就好了. 代码如下: #include <iostream> #include <cstdio> #include &l ...

随机推荐

  1. [XNUCA 进阶篇](web)writeup

    XNUCA 靶场练习题writeup default 阳关总在风雨后 题目过滤很多,*,#,/ ,and,or,|,union,空格,都不能用 盲注,最后的姿势是:1'%(1)%'1 中间的括号的位置 ...

  2. FPGA设计流程

    今天学习了FPGA设计流程的视频,我理解要做一个完整的FPGA系统,所要经历的步骤,先将它简单总结如下: 我在对上面的流程图进行解释: 第一:设计定义就是我们这个FPGA系统或者FPGA设计所要实现的 ...

  3. 12.9 Override

    12.9 Override 静态方法 父类的引用可以指向子类静态方法(用static修饰的方法)的调用只和左边定义的数据类型有关,如: public class Person { public sta ...

  4. brew 安装redis

    转:https://www.jianshu.com/p/e1e5717049e8 编辑新安装php的 p.p1 { margin: 0; font: 11px Menlo; color: rgba(0 ...

  5. Python使用pip安装No matching distribution found for PyYaml==5.3.1

    ERROR: Command errored out with exit status 1: command: /usr/local/dmahz/p_book_data/bin/python3.9 - ...

  6. spring-boot-learning-监控相关

    springboot提供了对项目的监控功能,首先我们需要引入需要的jar包: <!--监控包--> <!-- https://mvnrepository.com/artifact/o ...

  7. HTML 5中的输出元素是什么?

    当你需要计算两个输入的结果并将结果放到一个标签里的时候,就需要输出元素了.比如你有两个文本框(参见下图),你想要让这些文本框数字相加,然后输出给标签. 下面就是如何使用HTML 5代码输出元素. &l ...

  8. WSGI是个啥?大白话告诉你wsgi做了什么!

    定义: 官方定义:wsgi是Web服务器网关接口(Python Web Server Gateway Interface,缩写为WSGI)是为Python语言定义的Web服务器和Web应用程序或框架之 ...

  9. 学习heartbeat-03t实现web服务的高可用案例及维护要点

    8.Heartbeat实现web服务的高可用案例 8.1部署准备 通过web服务高可用案例来熟悉heatbeat软件的使用,用上面的两台服务器机器名分别为heartbeat-1-130和heartbe ...

  10. C++分布式系统——《开题》

    在下自大二接触编程,大二.大三刻苦涉猎编程相关书籍,自那时起爱上了 C++,C++确实极有魅力,本想从此在C++领域深钻,但是扩展技术的广度在那个算是半只脚踏入编程且已经读完了 C++ 流行书籍的阶段 ...