Description
有一位售票员给乘客售票,对于每位乘客,他会卖出多张连续的票,直到已卖出的编号的所有位置上的数的和不小于给定的正数k。然后他会按照相同的规则给下一位乘客售票。初始时,售票员持有的编号是从L到R的连续整数。请你求出,售票员可以售票给多少位乘客。
Input
三个整数L,R,k。
Output
一个正整数,表示能够拿到票的乘客数。
f[a][b][c]表示开头的和为b,末尾在[0,10a)的数,前面补上和为c的一段数字,能分出的段数和剩余的和
预处理a=0..19,b=0..190,c=0..k-1的答案
然后类似zkw线段树查出区间[l,r]对应的信息,合并答案
#include<cstdio> typedef long long i64; i64 l,r,p10[20]; struct state{ i64 c; int r; void operator+=(state w){ c+=w.c; r=w.r; } }f[21][197][1007]; struct pos{ int a,b; }stk1[10007],stk2[10007]; int stp1=0,stp2=0; int p1=0,p2=0; int k,sl[20],pl=0,sr[20],pr=0; int main(){ scanf("%lld%lld%d",&l,&r,&k); p10[0]=1; for(int i=1;i<=19;++i)p10[i]=p10[i-1]*10; for(int j=0;j<=190;++j){ for(int a=0;a<k;++a){ f[0][j][a]=(state){a+j>=k,a+j>=k?0:a+j}; } } for(int i=1;i<=19;++i){ for(int j=0;j<=190-i*9;++j){ for(int a=0;a<k;++a){ state&w=f[i][j][a]=f[i-1][j][a]; for(int b=1;b<10;++b){ w+=f[i-1][j+b][w.r]; } } } } --l;++r; while(l)sl[++pl]=l%10,l/=10; while(r)sr[++pr]=r%10,r/=10; pl=pr; int eq=pr; while(sl[eq]==sr[eq])--eq; int cl=0,cr=0; for(int i=1;i<=pr;++i)cl+=sl[i],cr+=sr[i]; for(int i=1;i<eq;++i){ cl-=sl[i],cr-=sr[i]; for(int a=sl[i]+1;a<=9;++a)stk1[stp1++]=(pos){i-1,cl+a}; for(int a=sr[i]-1;a>=0;--a)stk2[stp2++]=(pos){i-1,cr+a}; } cr-=sr[eq]; for(int a=sl[eq]+1;a<sr[eq];++a)stk1[stp1++]=(pos){eq-1,cr+a}; while(stp2)stk1[stp1++]=stk2[--stp2]; state w=(state){0,0}; for(int i=0;i<stp1;++i)w+=f[stk1[i].a][stk1[i].b][w.r]; printf("%lld\n",w.c); return 0; }