0
点赞
收藏
分享

微信扫一扫

1799: [Ahoi2009]self 同类分布 数位DP

王传学 2023-02-08 阅读 51


1799: [Ahoi2009]self 同类分布

Time Limit: 50 Sec  Memory Limit: 64 MB
Submit: 554  Solved: 194
[​​​Submit​​​][​​Status​​]

Description

给出a,b,求出[a,b]中各位数字之和能整除原数的数的个数。

Input

Output

Sample Input

10 19
 

Sample Output

3

HINT

【约束条件】1 ≤ a ≤ b ≤ 10^18

Source

​​Day1​​

 

分析:

最多时18个9,也就是数位和最大值是。162考虑枚举数位之和m。对于每个m,问题简化为有多少个数数位之和==m,且%m=0。想想状态:dp[pos][sum][val],当前pos位上数位和是sum,val就是在算这个数%mod,(从高位算  *10+i),因为我们枚举的数要保证数位和等于mod,还要保证这个数是mod的倍数,很自然就能找到这些状态,显然对于每一个mod,val不能保证状态唯一,这是你要是想加一维dp[pos][sum][val][mod],记录每一个mod的状态(这里sum可以用减法,然而val不行,就只能加一维),那你就想太多了,这样是会超时的(因为状态太多,记忆化效果不好)。这里直接对每一个mod,memset一次就能ac。下面的代码还把limit的当做了状态,因为每次都要初始化,所以能这样,memset在多组外面是不能这样的,不过奇葩的,这代码,如果不把limit当状态,还是在!limit 条件下记录dp,提交一发,时间竟然更短了,可能是每次memset的关系!!!

代码实现:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<string>

using namespace std;

typedef long long ll;

ll dp[20][163][163][2];
int a[20];
ll dfs(int pos,int sum,int val,int mod,bool limit)
{
if(sum-9*pos-9>0) return 0;
//最坏的情况,这一位及后面的全部为9都不能达到0那就直接GG,这个剪枝不会影响ac
if(pos==-1) return sum==0 && val==0;
if(dp[pos][sum][val][limit]!=-1) return dp[pos][sum][val][limit];
int up=limit?a[pos]:9;
ll ans=0;
for(int i=0;i<=up;i++)
{
if(sum-i<0) break;
ans+=dfs(pos-1,sum-i,(val*10+i)%mod,mod,limit && i==a[pos]);
}
dp[pos][sum][val][limit]=ans;
return ans;
}
ll solve(ll x)
{
int pos=0;
while(x)
{
a[pos++]=x%10;
x/=10;
}
ll ans=0;
for(int i=1;i<=pos*9;i++)//枚举数位和,上限就是每一位都是9。
{
memset(dp,-1,sizeof dp);

ll tmp=dfs(pos-1,i,0,i,true);
ans+=tmp;
}
return ans;
}
int main()
{
// cout<<18*9<<endl;
ll le,ri;
// memset(dp,-1,sizeof dp);
while(~scanf("%lld%lld",&le,&ri))
printf("%lld\n",solve(ri)-solve(le-1));
return 0;
}
/*
1 1000000000000000000
*/

 

举报

相关推荐

0 条评论