题意:
给出一个区间,将这个区间连续的数的数位加起来,保证和要不小于k,求能分的最多区间。
题解:
详细可以看算法合集之《浅谈数位类统计问题》,这题我们将数变成一个k进制的树,然后在树上考虑问题,对于这样的区间和,相当于树上子树的合并问题,因为合并会出现上个子树还有剩余的数位和,于是我们要合理利用这些数位和,dp[pos][sum][rem]表示数位pos,和为sum,前一棵子树还剩余rem数位和,对应的区间个数。
用记忆优化比较好些点。对于dp[pos[sum][rem]我们要记录两个状态,一个数cnt区间数,一个数rem上一颗子树剩余数位和。将dp定义为结构体。
#include<iostream>
#include<math.h>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<vector>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define B(x) (1LL<<(x))
void cmax(int& a,int b){ if(b>a)a=b; }
void cmin(int& a,int b){ if(b<a)a=b; }
typedef long long ll;
const int oo=0x3f3f3f3f;
const int MOD=1000000007;
int l[22],r[22],k;
struct DP{
ll cnt,rem;
DP(){cnt=rem=0;}
DP(ll c,ll r){
cnt=c;
rem=r;
}
DP operator+=(const DP& a){
cnt+=a.cnt;
rem=a.rem;
return *this;
}
}dp[22][205][1005];
void Init(){
for(int i=0;i<22;i++){
for(int j=0;j<205;j++){
for(int k=0;k<1005;k++){
dp[i][j][k].cnt=dp[i][j][k].rem=-1;
}
}
}
}
DP dfs(int pos,ll sum,ll rem,int f1,int f2){
if(pos<1){
if(sum+rem>=k) return DP(1,0);
else return DP(0,sum+rem);
}
if(!f1&&!f2&&dp[pos][sum][rem].cnt!=-1)return dp[pos][sum][rem];
DP res(0,rem);
int low = f1 ? l[pos] : 0;
int high= f2 ? r[pos] : 9;
for(int i=low;i<=high;i++){
res+=dfs(pos-1,sum+i,res.rem,f1&&i==low,f2&&i==high);
}
if(!f1&&!f2)dp[pos][sum][rem]=res;
return res;
}
ll solve(ll a,ll b){
Init();
int len=0;
while(b){
r[++len]=b%10;
b/=10;
}
int t=0;
while(a){
l[++t]=a%10;
a/=10;
}
for(int i=t+1;i<=len;i++)l[i]=0;
return dfs(len,0,0,1,1).cnt;
}
int main(){
/*
#define ON 1
#ifdef ON
freopen("E:\\read.txt","r",stdin);
#endif // ON
//*/
ll a,b;
while(scanf("%lld %lld %d",&a,&b,&k)!=EOF){
printf("%lld\n",solve(a,b));
}
return 0;
}
/**
*/