数位DP入门:bzoj1833: [ZJOI2010]count 数字计数

膜拜了一下蔡大神。。。。然后突然想起来一些东西然后就填了一个半年多前的坑= =

人生第一道自己写的数位DP。。。好吧以前是看题解然后也不知道为什么就过了的>_<

数位DP介绍:

http://wenku.baidu.com/link?url=9OS5Ybpw5wx00ahrH8ED2oyIlR1uWwrxT8N4pEg27GgBt2T2hLe4sd_h1rmpY7P0HmeHIEDw9h6_K98dPhhjoMhD2TpKcS8w1X8cC_dkPp_

接下来是题目地址:

http://www.lydsy.com:808/JudgeOnline/problem.php?id=1833

题意挺明显。。

首先我萌可以用F[i,j,k]表示有i位数字且前导为k的数中总共含有多少个数字j(0<=j<=9)。。。

然后蒟蒻不想太麻烦(太弱)。。。干脆把1~9拆成9次一次一次来。。。。

因为题目要求的是[l,r],所以答案就是[0,r]-[0,l-1]......

假设当前求的是0~x的数中含有多少个数字num。。。

首先设x有w位数字,把x拆成w位(第一位为最低位)存在数组h里面,然后在处理出x的后i位组合起来是什么数(也就是x的w个后缀)

用pre[i]表示i位数中含有数字i的个数

那么假设处理到第i(i从最高位(w)开始枚举)位:

1、先考虑之后位上的情况:

当第i位取的数字小于h[i]的时候,有h[i]种情况(0~h[i]-1),此时之后的所有数都可以选(之后不管选什么数都不超过x),所以总个数+=h[i]*pre[i-1](当前位每一种情况都能给总个数贡献之后位中数字个数)

2、考虑当前位的情况:

如果当前位最大能取的数字=num,由于之前位数都最大,之后的数不能超出x的范围(若之前位数有任意一个小于最大值,那么之后的数可以随便选,则已被上面的步骤计算到),所以能贡献出(当前再后一个后缀的值+1(因为还有一种情况是后面的全选0))个答案。。。。

如果当前位最大能取的数字<num,那么不论之后怎么取,当前这一位都不会贡献任何个数。。。

蒟蒻扯半天最终确定自己是语死早了>_<。。。。

下面是更丑的代码TAT

如果当前位最大能取的数字>num,那么当取的数字为num的时候后面的数可以随便取,且每种情况都会额外贡献一个个数(也就是当前位上的这个),所以总个数+=10^(i-1)

 1 var
 2   pre,orz,h,sum:array[0..13]of int64;
 3   ans,ans1:array[0..9]of int64;
 4   i,j,k,n,m,mid:longint;
 5   l,r,tot,tot1,l1,r1,x,w1,w2:int64;
 6 procedure mahoshojo(x,num,w:int64);
 7 var i:longint;anss:int64;
 8 begin
 9   anss:=0;
10   for i:=1 to w do h[i]:=x div orz[i-1] mod 10;
11   for i:=1 to w do sum[i]:=x mod orz[i];
12   for i:=w downto 1 do
13   begin
14     inc(anss,pre[i-1]*h[i]);
15     if h[i]>num then inc(anss,orz[i-1]);
16     if h[i]=num then inc(anss,sum[i-1]+1);
17   end;
18   ans[num]:=ans[num]+anss;
19   tot:=tot-anss;
20 end;
21 begin
22   pre[1]:=1;
23   orz[0]:=1;
24   for i:=1 to 13 do orz[i]:=orz[i-1]*10;
25   for i:=2 to 13 do pre[i]:=pre[i-1]*10+orz[i-1];
26   readln(l,r);
27   dec(l);
28   tot:=1;
29   if l>0 then
30   begin
31     l1:=l;
32     while l1>0 do begin inc(w1);l1:=l1 div 10 end;
33     for i:=1 to w1-1 do inc(tot,(orz[i]-orz[i-1])*i);
34     if w1>1 then  inc(tot,w1*(l-orz[w1-1]+1))  else tot:=l+1;
35   end
36   else
37     w1:=1;
38   r1:=r;
39   while r1>0 do begin inc(w2);r1:=r1 div 10 end;
40   tot1:=1;
41   for i:=1 to w2-1 do inc(tot1,(orz[i]-orz[i-1])*i);
42   if w2>1 then  inc(tot1,w2*(r-orz[w2-1]+1)) else tot1:=r+1;
43 //  writeln(w2,‘:‘,tot1,‘   ‘,w1,‘:‘,tot);
44   for i:=1 to 9 do
45   mahoshojo(l,i,w1);
46   for i:=1 to 9 do ans[i]:=-ans[i];
47   ans[0]:=tot;
48 //  writeln(‘ ‘,tot);
49   tot:=tot1;
50 //  writeln(‘!!!‘,tot1);
51   for i:=1 to 9 do
52   mahoshojo(r,i,w2);
53   ans[0]:=tot-ans[0];
54   for i:=0 to 8 do write(ans[i],‘ ‘);
55   writeln(ans[9])
56 end.

话说记得类似的还有一道GDOI的题。。。。要求完全相反= =。。。给你1~某个数字中出现的各个数码的次数,要求判断是否存在这个数。。。

反正蒟蒻目测只会二分乱搞TAT.。。。。反正怎么玩都不会TLE2333

//四个月没碰键盘TAT。。。脑子还剩一点但是代码能力已经回档到两年前了>_<

//话说考前复习的时候来填坑显然花样作大死。。。YCL:哪有你这样的学生啊!。。。。。。。。

时间: 2024-11-03 21:24:03

数位DP入门:bzoj1833: [ZJOI2010]count 数字计数的相关文章

bzoj1833: [ZJOI2010]count 数字计数(数位DP+记忆化搜索)

1833: [ZJOI2010]count 数字计数 题目:传送门 题解: 今天是躲不开各种恶心DP了??? %爆靖大佬啊!!! 据说是数位DP裸题...emmm学吧学吧 感觉记忆化搜索特别强: 定义f[i][j][k]表示若前i个位置有k个j的此时的全局方案数,然后就可以记忆化搜索了(具体看代码吧) 代码: 1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<cmath>

BZOJ1833 [ZJOI2010]count 数字计数 【数学 Or 数位dp】

题目 给定两个正整数a和b,求在[a,b]中的所有整数中,每个数码(digit)各出现了多少次. 输入格式 输入文件中仅包含一行两个整数a.b,含义如上所述. 输出格式 输出文件中包含一行10个整数,分别表示0-9在[a,b]中出现了多少次. 输入样例 1 99 输出样例 9 20 20 20 20 20 20 20 20 20 提示 30%的数据中,a<=b<=10^6: 100%的数据中,a<=b<=10^12. 题解 你以为我真的会写数位dp? 首先容斥一下,转化为求小于等于

BZOJ1833: [ZJOI2010]count 数字计数 (数位dp)

传送门 数位dp... ...大概都是这个套路吧... ... 写这个的时候直接水了一发... ...我也不知道自己写的是不是dp... ... 大概是主要内容和dp关系不大的dp... ... mark代码..细长的代码真是丑啊..换行太频繁了.... 1 #include<cmath> 2 #include<cstdio> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm&g

bzoj1833: [ZJOI2010]count 数字计数 &amp;&amp; codevs1359 数字计数

bzoj1833 codevs1359 这道题也是道数位dp 因为0有前导0这一说卡了很久 最后发现用所有位数减1~9的位数就okay.....orzczl大爷 其他就跟51nod那道统计1出现次数一样啦 #include<cstdio> #include<cstring> #include<algorithm> #define LL long long using namespace std; LL read(){ LL ans=0,f=1,c=getchar();

bzoj1833 [ZJOI2010]count 数字计数

题目链接 我是DP弱者!!!我是DP弱者!!!我是DP弱者!!! 调了好久,数位DP好恼火QAQ 1 #include<algorithm> 2 #include<iostream> 3 #include<cstdlib> 4 #include<cstring> 5 #include<cstdio> 6 #include<string> 7 #include<cmath> 8 #include<ctime> 9

【BZOJ-1833】count数字计数 数位DP

1833: [ZJOI2010]count 数字计数 Time Limit: 3 Sec  Memory Limit: 64 MBSubmit: 2494  Solved: 1101[Submit][Status][Discuss] Description 给定两个正整数a和b,求在[a,b]中的所有整数中,每个数码(digit)各出现了多少次. Input 输入文件中仅包含一行两个整数a.b,含义如上所述. Output 输出文件中包含一行10个整数,分别表示0-9在[a,b]中出现了多少次.

BZOJ 1833: [ZJOI2010]count 数字计数( dp )

dp(i, j, k)表示共i位, 最高位是j, 数字k出现次数. 预处理出来. 差分答案, 对于0~x的答案, 从低位到高位进行讨论 ------------------------------------------------------------------------------ #include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 16; const int N =

1833: [ZJOI2010]count 数字计数

1833: [ZJOI2010]count 数字计数 Time Limit: 3 Sec  Memory Limit: 64 MBSubmit: 2951  Solved: 1307[Submit][Status][Discuss] Description 给定两个正整数a和b,求在[a,b]中的所有整数中,每个数码(digit)各出现了多少次. Input 输入文件中仅包含一行两个整数a.b,含义如上所述. Output 输出文件中包含一行10个整数,分别表示0-9在[a,b]中出现了多少次.

【BZOJ 1833】 [ZJOI2010]count 数字计数

1833: [ZJOI2010]count 数字计数 Time Limit: 3 Sec Memory Limit: 64 MB Submit: 1697 Solved: 753 [Submit][Status][Discuss] Description 给定两个正整数a和b,求在[a,b]中的所有整数中,每个数码(digit)各出现了多少次. Input 输入文件中仅包含一行两个整数a.b,含义如上所述. Output 输出文件中包含一行10个整数,分别表示0-9在[a,b]中出现了多少次.