【SGU 390】Tickets (数位DP)

Tickets

Description

Conductor is quite a boring profession, as all you have to do is just to sell tickets to the passengers. So no wonder that once upon a time in a faraway galaxy one conductor decided to diversify this occupation. Now this conductor sells several tickets at a time to each passenger. More precisely, he successively gives tickets to the passenger until the sum of the digits on all the tickets given becomes not less than some integer number k. Then this process repeats for the next passenger. Initially conductor has a tape of tickets numbered successively from l to r, inclusive. This way of tickets distribution is quite good, because passengers are glad to get several tickets when they pay only for one. But there is one disadvantage. Since each passenger gets several tickets, it is possible that conductor won‘t be able to serve all passengers. Your task is to help conductor in this difficult situation. You should calculate how many passengers is the conductor able to serve.

Input

Input file contains three integer numbers lr and k (1 ≤ l ≤ r ≤ 10 18, 1 ≤ k ≤ 1000).

Output

Output should contain exactly one number — the answer to the problem.

Sample Input

sample input
sample output
40 218 57
29

【题意】

  

【分析】

  这题真难想啊,光是理解题解就理解了好久。。

  这题跟前一题是相似的,都是划分区间,但是这题k很小,意味着区间很多,不能一个个区间跑的。这能模拟填数得到答案。模拟填数的话画一个树形图很重要。

  一开始觉得画不画树都没什么关系,但是这题画了树真的好理解很多。因为这一题不能用ans[r]-ans[l],只能DP填l~r之间的数,而且要按从小到大的顺序,所以画树形图就很好看,可以拆成多个子树的并。

  以下图为例,(假设是棵二叉树,好画点,做题的时候实际上是十叉的)。

  

红色路径是L,绿色路径是R。我们填数实际上是从左边的绿色子树一直到右边的蓝色子树。

把原问题分解成多棵子树的子问题在并起来就好了。时间就会很快,是2*logn。

问题就是怎么把子树并起来,一开始会以为不可以,因为后子树的答案和前子树有关。但是我们注意到k很小,不妨记录一下前子树最后一个区间的空间,然后求后子树的答案。

然后这个子树其实是填数过程的后缀那部分的数,我们知道子树的位置所以也知道填数部分的前缀,这个对求子树答案是有影响的,我们子树上的每个数都要加上一个固定的数,所以也把他记录一下。即f[i][j][k]表示高度为i的子树,前子树最后一组剩余空间为j,每个数都要加上k时的答案(我们当作这棵子树的根权值为1,如果不为1而为i则把i加进k里面)。

f[[i][j][k]要记录分了多少组和最后一部分剩余多少,分别记为f[i][j][k].a,f[i][j][k].b

则f[i][j][k]=并(f[i][j][k],f[i-1][f[i][j][k].a][k+l]) (0<=l<=9)

并的话就很容易啦~~~

按顺序求答案就好啦~~

真是太厉害了,其实也不是很难,怎么我想不到,可能是因为之前都是做ans[r]-ans[l]的吧!!

代码如下:

  1 #include<cstdio>
  2 #include<cstdlib>
  3 #include<cstring>
  4 #include<iostream>
  5 #include<algorithm>
  6 #include<queue>
  7 #include<cmath>
  8 using namespace std;
  9 #define LL long long
 10
 11 LL l,r;
 12 int k,st;
 13 int al[20],ar[20];
 14 int hl[20],hr[20];
 15
 16 void init()
 17 {
 18     for(int i=1;i<=18;i++)
 19     {
 20         al[i]=(int)l%10;l/=10;
 21         ar[i]=(int)r%10;r/=10;
 22     }
 23     for(st=18;st>=1;st--) if(al[st]!=ar[st]) break;
 24     st++;
 25     hl[19]=hr[19]=0;
 26     for(int i=18;i>=1;i--)
 27       hl[i]=hl[i+1]+al[i],hr[i]=hr[i+1]+ar[i];
 28 }
 29
 30 struct node
 31 {
 32     LL sm;
 33     int rm;
 34     node ()
 35     {
 36         sm=-1;
 37     }
 38 };
 39 node f[20][210][1010];
 40 node ans,rm;
 41
 42 node get_ans(node x,node y)//combine
 43 {
 44     x.sm+=y.sm;
 45     x.rm=y.rm;
 46     return x;
 47 }
 48
 49 node get_f(int x,int y,int z)//x->height y->add z->room
 50 {
 51     if(f[x][y][z].sm!=-1) return f[x][y][z];
 52     node now;
 53     if(x==1)
 54     {
 55         now.sm=(y+z>=k)?1:0;
 56         now.rm=(y+z>=k)?0:y+z;
 57     }
 58     else
 59     {
 60         now=get_f(x-1,y,z);
 61         for(int i=1;i<10;i++)
 62         {
 63             now=get_ans(now,get_f(x-1,y+i,now.rm));
 64         }
 65     }
 66     return now;
 67 }
 68
 69 void ffind()
 70 {
 71     ans.sm=(hl[1]>=k)?1:0;ans.rm=(hl[1]>=k)?0:hl[1];
 72     for(int i=2;i<st;i++)
 73     {
 74         for(int j=al[i-1]+1;j<10;j++)
 75         {
 76             ans=get_ans(ans,get_f(i-1,hl[i]+j,ans.rm));
 77         }
 78     }
 79     for(int i=al[st-1]+1;i<ar[st-1];i++)
 80     {
 81         ans=get_ans(ans,get_f(st-1,hl[st]+i,ans.rm));
 82     }int i;
 83     for(i=st-1;i>=1;i--)
 84     {
 85         for(int j=0;j<ar[i-1];j++)
 86         {
 87             ans=get_ans(ans,get_f(i-1,hr[i]+j,ans.rm));
 88         }
 89     }
 90     if(hr[1]+ans.rm>=k) ans.sm++;
 91     printf("%lld\n",ans.sm);
 92 }
 93
 94 int main()
 95 {
 96     freopen("a.in","r",stdin);
 97     freopen("a.out","w",stdout);
 98     scanf("%lld%lld%d",&l,&r,&k);
 99     init();
100     ffind();
101     return 0;
102 }

[SGU 390]

【其实我没有交,交不了...但是拍了感觉没什么问题..吧



还是放大神题解吧:

时间: 2024-10-23 19:31:34

【SGU 390】Tickets (数位DP)的相关文章

SGU 390-Tickets(数位dp)

题意:有标号l-r的票,要给路人发,当给的票的编号的各数位的总和(可能一个人多张票)不小k时,才开始发给下一个人,求能发多少人. 分析:这个题挺难想的,参考了一下题解,dp[i][sum][left] 长度i 当前数位和sum  前一子树剩余的和 #include <map> #include <set> #include <list> #include <cmath> #include <queue> #include <stack>

【数位DP】Codeforces Gym 100418J Lucky tickets

题意: 设性质P:一个数能够整除它二进制表示下的1的个数.求[1,N]中满足性质P的数的个数.N<=1019. 思路: 数位DP.首先这个数最多有64位,我们可以枚举1的个数x,然后求可以整除x的数的个数.设dp[i][j][k][w]表示从最高位枚举到i位,现在已经构成的数模x余多少(这里是关键,只用考虑余数),现在已经用了k个1,w=0表示现在枚举的这个数已经小于N了,w=1表示从最高位到第i位都与N相同(数位dp计算时的方法:如果当前枚举的数小于N了,那么枚举的这一位就可以任意,如果等于N

Gym 100418J Lucky tickets(数位dp)

题意:给定一个n,求区间[1, n]之间的所有的a的个数,a满足: a能整除  把a表示自身二进制以后1的个数 思路:题意很绕.... 数位dp,对于所有可能的1的个数我们都dfs一次 对于某一个可能的1的个数p来说,状态dp(len, i, j, k)里的每一位分别表示当前位,当前确定位的值模除p,已经有了多少个1,是否已经小于给定的n, 注意的是这题范围很大,要用unsigned long long 来定义n #include<cstdio> #include<cstring>

51Nod 1009 数字1的个数 | 数位DP

题意: 小于等于n的所有数中1的出现次数 分析: 数位DP 预处理dp[i][j]存 从1~以j开头的i位数中有几个1,那么转移方程为: if(j == 1) dp[i][j] = dp[i-1][9]*2+pow(10,i-1);else dp[i][j] = dp[i-1][9]+dp[i][j-1]; 然后注意下对于每个询问统计的时候如果当前位为1需要额外加上他后面所有位数的个数,就是n%pow(10,i-1); 这样总复杂度log(n)*10 #include <bits/stdc++.

HDU 3555 Bomb (数位DP)

数位dp,主要用来解决统计满足某类特殊关系或有某些特点的区间内的数的个数,它是按位来进行计数统计的,可以保存子状态,速度较快.数位dp做多了后,套路基本上都差不多,关键把要保存的状态给抽象出来,保存下来. 简介: 顾名思义,所谓的数位DP就是按照数字的个,十,百,千--位数进行的DP.数位DP的题目有着非常明显的性质: 询问[l,r]的区间内,有多少的数字满足某个性质 做法根据前缀和的思想,求出[0,l-1]和[0,r]中满足性质的数的个数,然后相减即可. 算法核心: 关于数位DP,貌似写法还是

51nod1043(数位dp)

题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1043 题意:中文题诶- 思路:数位dp 我们用dp[i][j]来存储长度为2*i且一半和为j的所有情况(包括前导0的情况),为了方便我们现在只讨论其一半的和的情况,因为如果包括前导0的话其两边的情况是一样的: 我们假设再长度为i-1的数字最前面加1位数字k,0<=k<=9(这位数字加在哪里并不影响答案,因为我们在计算i-1长度的时候已经计算了所有组合情况,

数位dp

1.[hdu3709]Balanced Number 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<string> 5 #include<cstdlib> 6 #include<algorithm> 7 #include<ctime> 8 #include<cmath> 9 #include<queue>

【HDU 3652】 B-number (数位DP)

B-number Problem Description A wqb-number, or B-number for short, is a non-negative integer whose decimal form contains the sub- string "13" and can be divided by 13. For example, 130 and 2613 are wqb-numbers, but 143 and 2639 are not. Your task

hdu 5898 odd-even number 数位DP

odd-even number Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 716    Accepted Submission(s): 385 Problem Description For a number,if the length of continuous odd digits is even and the length