20230719-动态规划DP

发布时间 2023-07-20 14:26:28作者: H_W_Y

20230719

数位DP

P4127 [AHOI2009] 同类分布

题目描述

传送门
求出 [a,b] 中各位数字之和能整除原数的数的个数 \(a,b ≤ 1e18\)

Solution

对于这种求是否能整除的题
我们只有在最后才能得到答案

这道题很明显是数位DP
考虑用记忆化搜索来实现
对于每一位我们需要维护前面的数字之和和前面所组成的数
而由于前面所组成的数太大了,可以到达\(1e18\)
但是数字之和又一定\(\le 9* 18\)

我们就考虑取模数
这样进行\(9 * 18\)次dfs即可
每一次要判断数字之和\(=mod\)
这样记录答案就可以了

H_W_Y-Coding
#include <bits/stdc++.h>
using namespace std;
#define ll long long

ll a,b,mod,dp[20][185][185];
int len=0,p[20];

ll dfs(int id,int sum,ll st,int limit){
  if(id>len) return st==0&&sum==mod?1:0;
  if(!limit&&dp[id][sum][st]!=-1) return dp[id][sum][st];
  int u=limit?p[id]:9;
  ll res=0;
  for(int i=0;i<=u;i++)
    res+=dfs(id+1,sum+i,(st*10+i)%mod,limit&&i==u);
  if(!limit) dp[id][sum][st]=res;
  return res;
}

ll solve(ll x){
  len=0;
  while(x){
  	p[++len]=x%10;
  	x/=10;
  }
  ll res=0;
  for(int i=1;i<=len/2;i++) swap(p[i],p[len-i+1]);
  for(mod=1;mod<=9*len;mod++){
    memset(dp,-1,sizeof(dp));
    res+=dfs(1,0,0,1);
  }
  return res;
}

int main(){
  /*2023.7.19 H_W_Y P4127 [AHOI2009] 同类分布 数位DP*/ 
  scanf("%lld%lld",&a,&b);
  printf("%lld\n",solve(b)-solve(a-1));
  return 0;
}