【题目】
题目描述:
农民约翰的牛正开始一个美妙的旅程。牛车的里程表上显示一个整数表示里程,旅程开始时里程数为 x ( 100 ≤ x ≤ 1 0 18 ) x(100 \le x \le 10^{18}) x(100≤x≤1018),结束时里程数为 y ( x ≤ y ≤ 1 0 18 ) y(x \le y \le 10^{18}) y(x≤y≤1018)。每当里程表显示一个有趣的数时(包括起点和终点数),牛们会发出愉快的叫声。
对于一个里程数的每一位,如果有至少一半的数字时相同的,则这个里程数一个有趣的数。例如: 3223 3223 3223 和 110 110 110 是有趣的数,而 97791 97791 97791 和 123 123 123 则不是。
请计算,整个旅程中,牛们会发出多少吃愉快的叫声。
输入格式:
一行两个数,分别表示 x , y x,y x,y。
输出格式:
一行一个数,表示答案。
样例数据:
输入
110 133
输出
14
【分析】
看到题目就知道这是一道数位 D P \mathrm{DP} DP的题。
那在 D P \mathrm{DP} DP 的时候,怎么来计算有没有某一个数字有没有超过一半呢?(这也是考场上一直困扰我的问题。)
我们可以枚举每个数( 0 0 0 ~ 9 9 9),就让这个数超过一半(将这个数设为 x x x)。
这样,问题就转化成了,计算 [    l , r    ] [\;l,r\;] [l,r] 中,各个位上 x x x 超过一半的数的个数,这就是一道简单题了。
然后把 0 0 0 ~ 9 9 9 中的答案累加一下就好了。
然后注意两点:
- 对于 1919 1919 1919 这样的数,枚举 1 1 1 的时候会算一次,枚举 9 9 9 的时候会再算一次,就会算重,要减去重复的。
- 注意要除去前导零的影响。
【代码】
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
int a[20],A,B;
ll f[20][20][20][2][2];
ll dp1(int p,int num,int all,bool lead,bool limit)
{
if(!p) return lead?0:(num*2>=all);
if(~f[p][num][all][lead][limit]) return f[p][num][all][lead][limit];
int i,up=limit?a[p]:9;ll ans=0;
for(i=0;i<=up;++i)
{
if(lead&&i==0) ans+=dp1(p-1,num,all-1,true,limit&&a[p]==i);
else ans+=dp1(p-1,num+(i==A),all,false,limit&&a[p]==i);
}
f[p][num][all][lead][limit]=ans;
return ans;
}
ll g[20][20][20][20][2][2];
ll dp2(int p,int num1,int num2,int all,bool lead,bool limit)
{
if(!p)
{
if(lead) return 0;
if(all&1) return 0;
if(num1*2==all&&num2*2==all) return 1;
return 0;
}
if(~g[p][num1][num2][all][lead][limit]) return g[p][num1][num2][all][lead][limit];
int i,up=limit?a[p]:9;ll ans=0;
for(i=0;i<=up;++i)
{
if(lead&&i==0) ans+=dp2(p-1,num1,num2,all-1,true,limit&&a[p]==i);
else ans+=dp2(p-1,num1+(i==A),num2+(i==B),all,false,limit&&a[p]==i);
}
g[p][num1][num2][all][lead][limit]=ans;
return ans;
}
ll solve(ll x)
{
int i,j,p=0;ll ans=0;
while(x) a[++p]=x%10,x/=10;
for(i=0;i<=9;++i)
{
memset(f,-1,sizeof(f));
A=i,ans+=dp1(p,0,p,true,true);
}
for(i=0;i<=9;++i)
{
A=i;
for(j=i+1;j<=9;++j)
{
memset(g,-1,sizeof(g));
B=j,ans-=dp2(p,0,0,p,true,true);
}
}
return ans;
}
int main()
{
ll l,r;
scanf("%lld%lld",&l,&r);
printf("%lld",solve(r)-solve(l-1));
return 0;
}