题意:
给出一个区间,将这个区间连续的数的数位加起来,保证和要不小于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; } /** */