Problem
给出一个区间 ,问数位中连续奇数的长度为偶数并且连续偶数的长度为奇数的数的个数。
。
Solution
很明显的数位DP。
我一开始想的比较复杂,调了半天死活每调出来,后来看题解发现挺简单的。
我们用 表示当前在从高到低第 位, 表示前一位的奇偶( 表示偶, 表示奇), 表示连续奇数(或偶数)的长度的奇偶( 表示偶, 表示奇)。
那么,如果 这一位和上一位的奇偶相同,那么把 的奇偶变一下继续DP就好了。
如果 和上一位的奇偶不同,只有当 的奇偶和上一位的奇偶不同才行(否则不满足题意),此时将 改一下奇偶, 变为 再继续DP。
注意要考虑前导零的影响。
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
int T,a[20];
ll l,r,f[20][2][2];
ll dp(int p,int last,int parity,bool lead,bool limit)
{
if(!p) return last!=parity;
if(!lead&&!limit&&~f[p][last][parity]) return f[p][last][parity];
int i,up=limit?a[p]:9;ll ans=0;
for(i=0;i<=up;++i)
{
if(lead) ans+=dp(p-1,i&1,1,lead&&(!i),limit&&a[p]==i);
else if(last==(i&1)) ans+=dp(p-1,last,(parity+1)&1,false,limit&&a[p]==i);
else if(parity!=last) ans+=dp(p-1,last^1,1,false,limit&&a[p]==i);
}
if(!lead&&!limit) f[p][last][parity]=ans;
return ans;
}
ll solve(ll x)
{
int p=0;
while(x) a[++p]=x%10,x/=10;
return dp(p,0,1,true,true);
}
int main()
{
scanf("%d",&T);
memset(f,-1,sizeof(f));
for(int i=1;i<=T;++i)
{
scanf("%lld%lld",&l,&r);
printf("Case #%d: %lld\n",i,solve(r)-solve(l-1));
}
return 0;
}