【数位统计】Tickets

来源:互联网 发布:linux 当前目录复制 编辑:程序博客网 时间:2024/05/21 09:20
有一位售票员给乘客售票。对于每位乘客,他会卖出多张连续的票,直到已卖出的票的编号的数位之和不小于给定的正数K。然后他会按照相同的规则给下一位乘客售票。
—初始时,售票员持有的票的编号是从L到R的连续整数。请你求出,售票员可以售票给多少位乘客。
—数据规模:1 ≤ L ≤ R≤ 1018,1 ≤ K ≤1000。
刘聪论问题,以前做此类题都是按位考虑,可是偏偏此题用树考虑极为方便(居然不满足区间减法)。
由于是十进制,所以划分成十叉树,但是由于“数位之和不小于给定的正数K”,所以不一定等于,则对与一棵子树来说可能前几个节点会给别人,自己又会抢一些节点,因此多记录一维前多少的部分与上一组合并,在转移时将最后一组剩余多少记录一下然后类似线段树找到所需区间进行dp。
一开始我将线段树似的dfs和dp混在一起,结果状态给搞错了,本来想改进结果发现如果dfs与dp在一起则表示的状态量就很大了,所以干脆分开弄,反而清晰了不少。
#include <cstdio>#include <cstdlib>#include <cstring>struct tree {long long a,b;}f[20][200][1000],ans;const int oo=1073741819;int g[20][200][1000],s,a[40],b[40],c[40];long long k,l,r;tree merge(tree x,tree y) {  x.a+=y.a;  x.b=y.b;  return x;}void dp(int h,int sum,int rem){  int i;  g[h][sum][rem]=1;  if (h==s) {    if ((sum>=k)||(sum+rem>=k)) f[h][sum][rem].a=1,f[h][sum][rem].b=0;    else f[h][sum][rem].a=0,f[h][sum][rem].b=sum+rem;    return ;  }    f[h][sum][rem].b=rem;  for (i=0;i<=9;i++) {    if (!g[h+1][sum+i][f[h][sum][rem].b]) dp(h+1,sum+i,f[h][sum][rem].b);    f[h][sum][rem]=merge(f[h][sum][rem],f[h+1][sum+i][f[h][sum][rem].b]);  }}void dfs(int h,int sum,int lcal,int lcar){  int i,lcl,lcr,ll,rr;  if (((!lcal)&&(!lcar))||(h==s)) {    if (!g[h][sum][ans.b]) dp(h,sum,ans.b);    ans=merge(ans,f[h][sum][ans.b]);    return ;  }  if (lcal) ll=a[h+1];else ll=-oo;  if (lcar) rr=b[h+1];else rr=oo;  for (i=0;i<=9;i++)     if ((ll<=i)&&(i<=rr)) {      lcl=(ll==i) ? 1 : 0;      lcr=(rr==i) ? 1 : 0;      dfs(h+1,sum+i,lcl,lcr);    }}void init(){  long long i,j;  scanf("%I64d%I64d%I64d\n",&l,&r,&k);  for (s=0,i=l,j=r;(i)||(j);i/=10,j/=10) {    a[++s]=i%10,b[s]=j%10;  }  for (i=1;i<=s;i++) c[i]=a[i];  for (i=s;i>=1;i--) a[i]=c[s-i+1];  for (i=1;i<=s;i++) c[i]=b[i];  for (i=s;i>=1;i--) b[i]=c[s-i+1];    dfs(0,0,1,1);  printf("%I64d\n",ans.a);}int main(){  freopen("input.txt","r",stdin);  freopen("output.txt","w",stdout);    init();  return 0;}


原创粉丝点击