哈尔滨理工大学第七届程序设计竞赛决赛(网络赛-高年级组)D - 数圈圈

时间:2022-06-05 20:44:15

题目描述

tabris有一个习惯,无聊的时候就会数圈圈,无论数字还是字母。
现在tabris更无聊啦,晚上睡不着觉就开始数羊,从a只数到b只。
顺便还数了a到b之间有多少个圈。
 
但是tabris笨啊,虽然数羊不会数错,但很可能数错圈的个数。
但是tabris很难接受自己笨这个事实,所以想问问你他一共应该数出多少个圈,这样tabris才好判断他到底笨不笨啊。 

输入描述:

输入一个T,表示数据组数
每组测试数据包含两个正整数a,b。
T∈[1,1000]
a,b∈[1,1014]

输出描述:

每组数据输出结果,并换行。
示例1

输入

11
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
10 10
1 100

输出

0
0
0
1
0
1
0
2
1
1
111

备注:

数字的圈的个数请根据样例自行理解。

题解

数位$dp$。

$dp[i][j]$表示数字长度有$i$位,且第$i$位为$j$的总和,统计一下就可以了。

#include <bits/stdc++.h>
using namespace std;

long long dp[16][10];
long long b[20], a[20];
long long c[20], sz;

/*
0 1
1 0
2 0
3 0
4 1
5 0
6 1
7 0
8 2
9 1
*/

void init() {
  memset(a, 0, sizeof a);
  a[0] = 1;
  a[4] = 1;
  a[6] = 1;
  a[8] = 2;
  a[9] = 1;

  b[0] = 1;
  for(int i = 1; i <= 15; i ++) {
    b[i] = b[i - 1] * 10LL;
  }

  for(int i = 0; i <= 9; i ++) {
    dp[1][i] = a[i];
  }
  for(int i = 2; i <= 15; i ++) {
    for(int j = 0; j <= 9; j ++) {
      dp[i][j] = a[j] * b[i - 1];
      for(int k = 0; k <= 9; k ++) {
        dp[i][j] = dp[i][j] + dp[i - 1][k];
      }
    }
  }
}

long long get(long long x) {
  if(x == 0) return 0;
  long long ans = 0;
  sz = 0;
  while(x) {
    sz ++;
    c[sz] = x % 10;
    x = x / 10;
  }

  for(int i = 1; i <= sz - 1; i ++) {
    for(int j = 1; j <= 9; j ++) {
      ans = ans + dp[i][j];
    }
  }

  for(int j = 1; j < c[sz]; j ++) {
    ans = ans + dp[sz][j];
  }

  long long sum = a[c[sz]];
  for(int i = sz - 1; i >= 1; i --) {
    for(int j = 0; j < c[i]; j ++) {
      ans = ans + dp[i][j];
      ans = ans + sum * b[i - 1];
    }
    sum = sum + a[c[i]];
  }

  return ans;
}

int main() {
  init();
  int T;
  scanf("%d", &T);
  while(T --) {
    long long L, R;
    scanf("%lld%lld", &L, &R);
    printf("%lld\n", get(R + 1) - get(L));
  }
  return 0;
}