hdu5898 数位dp+预处理 odd-even number

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<time.h>
using namespace std;
#define ll long long
int a[25];
ll dp[25][2],tp[25][25],dpp[25][2],tpp[25];
ll pow(int k)
{
	ll num=1;
	for(int i=0;i<k;i++)
		num*=5;
	return num;
}
void init()
{
	dp[0][0]=0,dp[0][1]=dpp[0][1]=1;
	dp[1][1]=dp[2][0]=0;
	dpp[1][1]=dpp[2][0]=0;
	dpp[2][1]=25,dpp[1][0]=5;
	dp[2][1]=25,dp[1][0]=5;
	dp[3][1]=100,dp[3][0]=225;
	dpp[3][1]=125,dpp[3][0]=250;
	for(int i=4;i<20;i++)
	{
		dp[i][1]=(dp[i-2][0]+dp[i-2][1])*25;
		dp[i][0]=dp[i-1][1]*5+dp[i-2][0]*25;
		dpp[i][1]=(dpp[i-2][1]+dpp[i-2][0])*25;
		dpp[i][0]=dpp[i-1][1]*5+dpp[i-2][0]*25;
	}
	tpp[0]=1;
	for(int i=1;i<20;i++)
	{
		tp[0][i]=dpp[i][1];
		tpp[i]=tp[0][i];
		for(int j=2;j<=i;j+=2)
		{
			tp[j][i]=pow(j)*(dpp[i-j][1]);
			tpp[i]+=tp[j][i];
		}
	}
}
int f(int l,int r)
{
	int i,cnt1=0,cnt2=0,flag=a[l]&1;
	for(i=l;i<r;i++)
	{
		if((a[i]&1)!=flag)
        {
			if((cnt1&1)||((!(cnt2&1))&&cnt2))
				return 0;
			else
            {
                flag=a[i]&1;
                cnt1=cnt2=0;
                i--;
            }
		}
		else
		{
		    if(a[i]&1)
                cnt1++;
            else
                cnt2++;
		}
	}
	if((cnt1&1)||((!(cnt2&1))&&cnt2))
		return 0;
	return 1;
}
ll solve(ll x)
{
	int cnt=0,i,j;
	ll sum=0;
	memset(a,0,sizeof(a));
	while(x)
	{
		a[cnt++]=x%10;
		x/=10;
	}
	if(cnt<=1) return a[0]/2+1;
	for(i=1;i<cnt;i++)
        sum+=dp[i][0]+dp[i][1];
	for(i=0;i<cnt;i++)
	{
	    //cout<<sum<<endl;
		if(i+1==cnt)
		{
			sum+=a[i]/2*5*(dpp[i-1][0]+dpp[i-1][1]);
			sum+=(a[i]-1)/2*tpp[i];
			return sum;
		}
		int cntt=0;
		for(j=i+1;j<cnt&&((a[i+1]&1)==(a[j]&1));j++,cntt++);
		if(j>=cnt||f(j,cnt))
		{
			if(cntt&1)
			{
				if(a[i+1]&1)
				{
					if(i)sum+=a[i]/2*(dpp[i][0]+dpp[i][1]);
					else
						sum+=(a[i]+1)/2*(dpp[i][0]+dpp[i][1]);
				}
				else
				{
					if(i)
					{
						sum+=a[i]/2*5*(dpp[i-1][0]+dpp[i-1][1]);
						sum+=((a[i]+1)/2)*5*tpp[i-1];
					}
				}
			}
			else
			{
				if(a[i+1]&1)
				{
					if(i==0)
						sum+=a[i]/2+1;
					else
					{
						sum+=a[i]/2*5*(dpp[i-1][1]+dpp[i-1][0]);
						sum+=((a[i]+1)/2)*tpp[i];
					}
				}
				else
				{
					if(!i)
						sum+=a[i]/2+1;
					else
						sum+=((a[i]+1)/2)*tpp[i];
				}
			}

		}
	}
	return sum;
}
typedef long long llong;

const int MAX_DIGIT = 30;

int digit[MAX_DIGIT];
llong dppp[MAX_DIGIT][10][2][2];

//当前是第i位,前一位是pre,是否存在前导零,当前是否已经满足要求,是否需要限制
llong dfs(int i, int pre, bool zero, bool yes, bool limit)
{
    //此时如果满足条件则返回1个
    if (i == -1) {
        return yes;
    }
    //枚举到的最大数,不需要限制时为9
    int max_digit = limit ? digit[i] : 9;

    llong& dpnow = dppp[i][pre][zero][yes];
    //如果不需要限制且dp值不为-1,直接返回dp值,即记忆化
    if (!limit && dpnow != -1) {
        return dpnow;
    }

    llong ans = 0;
    //now==0时zero为true,now为偶数时yes为true,当前有限制且now为最大数则仍然需要限制
    if (zero) {
        for (int now = 0; now <= max_digit; ++now) {
            ans += dfs(i - 1, now, now == 0, !(now & 1), limit && now == max_digit);
        }
    }
    //此时zero一定为false,只有前一位是奇数now是偶数时yes为true,是否需要限制同上
    else if (yes) {
        for (int now = 0; now <= max_digit; ++now) {
            ans += dfs(i - 1, now, false, (pre & 1) && !(now & 1),
                       limit && now == max_digit);
        }
    }
    //当前是不满足条件的,为了满足条件,now必须跟pre奇偶性相同,此时yes一定为true
    else {
        for (int now = (pre & 1); now <= max_digit; now += 2) {
            ans += dfs(i - 1, now, false, true, limit && now == max_digit);
        }
    }
    //更新dp值,记忆化
    if (!limit) {
        dpnow = ans;
    }
    return ans;
}

llong count_odd_even(llong n)  //数0-n范围内有多少满足条件的数
{
    if (n == 0) {
        return 1;
    }
    int length = 0;
    //将n数位分离并存入digit数组
    for (; n; n /= 10) {
        digit[length] = n % 10;
        length += 1;
    }
    return dfs(length - 1, 0, true, true, true);
}

int main()
{
    //freopen("mydata.txt", "w", stdout);
	ll t,l,r,temp;
	srand(time(NULL));
	int cas=0;
	init();
	scanf("%I64d",&t);
	while(t--&&scanf("%I64d%I64d",&l,&r)!=EOF)
    {
        /*l=r=0;
        while(l>=r)
        {l=rand();
        r=rand();}
        if(solve(r)-solve(l-1)!=count_odd_even(r)-count_odd_even(l-1))
            cout<<l<<' '<<r<<endl;*/
        printf("Case #%d: %I64d\n",++cas,solve(r)-solve(l-1));
        //cout<<solve(r)<<endl;
	    /*for(r=10000;r<1e12;r+=10000)
        {
            temp=solve(r)-solve(0);
            printf("%I64d\n",temp);
        }*/
    }
	return 0;
}
/*
20434 21216
2725 23996
19777 23563
630 31544
10223 19980
5785 12387
3446 28823
2108 11804
3300 14079
4280 13758
*/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值