Codeforces Beta Round #51 D. Beautiful numbers(数位dp+思维)

版权声明:欢迎转载,若转载,请标明出处,如有错误,请指点,也欢迎大佬们给出优化方法 https://blog.csdn.net/Charles_Zaqdt/article/details/86531399

题目链接:http://codeforces.com/contest/55/problem/D

       题意就是给你一个范围,问这个范围内有多少个数是它各位非零数的倍数。

       思路就是数位dp,但是要求这个数要能整除各个非零位,这个状态不太好标记,所以这里就需要用一点数学知识了,一个数可以整出这个数的每一位非零数,那么只要可以整出每一位非零数的lcm就好了,然后我们可以算出1-9的lcm是2520,所以我们只需要去标记每个数的每一位数的lcm的状态就好了。这样的空间复杂度就缩小到了20*2520*2520,但是这样还是很大,然后我们发现1-9的lcm的个数并不多,所以这里又可以用离散化优化到20*30*2520,然后跑一遍就可以了。


AC代码:

#include <bits/stdc++.h>
#define ll long long
using namespace std;
int a[20];
ll dp[20][50][2550];
int Hash[2550];

void init(){
  int cnt = 0;
  memset(dp, -1, sizeof(dp));
  for(int i=1;i<=2520;i++){
    if(2520 % i == 0) Hash[i] = cnt++;
  }
}

ll lcm(ll x, ll y){
  return x / __gcd(x, y) * y;
}

ll dfs(int pos, int prelcm, int prenum, bool limit){
  if(pos == -1) return prenum % prelcm == 0;
  if(!limit && dp[pos][Hash[prelcm]][prenum] != -1)
    return dp[pos][Hash[prelcm]][prenum];
  int up = limit ? a[pos] : 9;
  ll ans = 0;
  for(int i=0;i<=up;i++){
    int nownum = (prenum * 10 + i) % 2520;
    int nowlcm = prelcm;
    if(i) nowlcm = lcm(prelcm, i);
    ans += dfs(pos - 1, nowlcm, nownum, limit && i == up);
  }
  if(!limit) dp[pos][Hash[prelcm]][prenum] = ans;
  return ans;
}

ll solve(ll x){
  int pos = 0;
  while(x){
    a[pos++] = x % 10;
    x /= 10;
  }
  return dfs(pos - 1, 1, 0, true);
}

int main()
{
  int T;
  scanf("%d",&T);
  init();
  while(T--){
    ll l, r;
    scanf("%lld%lld",&l, &r);
    printf("%lld\n", solve(r) - solve(l - 1));
  }
  return 0;
}

猜你喜欢

转载自blog.csdn.net/Charles_Zaqdt/article/details/86531399