#1033 : 交错和

#1033 : 交错和

时间限制: 10000ms
单点时限: 1000ms
内存限制: 256MB

描述

给定一个数 x,设它十进制展从高位到低位上的数位依次是 a0, a1, ..., an - 1,定义交错和函数:

f(x) = a0 - a1 + a2 - ... + ( - 1)n - 1an - 1

例如:

f(3214567) = 3 - 2 + 1 - 4 + 5 - 6 + 7 = 4

给定 

1405402477702.png

输入

输入数据仅一行包含三个整数,l, r, k(0 ≤ l ≤ r ≤ 1018, |k| ≤ 100)。

输出

输出一行一个整数表示结果,考虑到答案可能很大,输出结果模 109 + 7

提示

对于样例 ,满足条件的数有 110 和 121,所以结果是 231 = 110 + 121。

更多样例:

Input
4344 3214567 3
Output
611668829
Input
404491953 1587197241 1
Output
323937411
Input
60296763086567224 193422344885593844 10
Output
608746132
Input
100 121 -1
Output
120



样例输入
100 121 0
样例输出
231
Emacs Normal Vim


刚开始做的时候脑洞开大了以为是数论专题,后来才发现是数位dp,几个容易易卡住的点:

1.记忆化搜索写的时候要将相同交错和的个数,相同交错和的数字的和分别进行dp

2.对于一位数字和两位数字的计算方式并不相同,要分数字的位数进行讨论。

3.由于结果可能比较大,每一步都需要使用同余定理,以防运算过程中爆long long的情况。


记忆化搜索的思路,

当前的交错和相同的数字的和=sum(待搜索的状态的数字和+当前搜索的数字的大小*当前搜索到的符合条件的数字个数)。

#include <cstdio>
#include <cstring>
long long mod=1000000007;
long long base[20];
long long l,r,k,bit[20],bt,yy;
struct node {
    long long s,n;//s代表数字和,n代表数字个数
};
node dp[20][400];//状态转移
node dfs(long long pos,long long target,long long limit)//数位dp,基本可以算是模板啦
{
    node t;
    t.s=t.n=0;
    if (pos==0) {               //处理到最后一位,直接判断返回
        if (target==100)
        t.n=1;
        return t;
    }
    if ((limit==0)&&(dp[pos][target].n!=-1)) return dp[pos][target];
    long long tail=limit?bit[pos]:9;
    long long sgn=((yy-pos)%2)?(-1):(1);//确定符号
    long long head;
    if (pos==yy)head =1;
    else head=0;//确定搜索的起点和终点
    for (int i=head;i<=tail;i++)
    {
        node tmp=dfs(pos-1,target-i*sgn,(limit==1)&&(i==bit[pos]));
        if ((tmp.n)>0){
            t.n+=tmp.n;
            long long q;
            q=((((tmp.n%mod)*base[pos])%mod)*i)%mod;//结果的同余处理
            t.s+=(tmp.s)%mod;
            t.s%=mod;
            t.s+=q;
            t.s%=mod;//每一步都要同余
        }
    }
    if (limit==0) dp[pos][target]=t;
    return t;
}
long long cal(long long x,long long y)
{
    long long ans=0;
    if (x==-1) return 0;
    if (x==0) return 0;
    bt = 0;
    while (x)
    {
        bt++;
        bit[bt]=x%10;
        x/=10;
    }
    for (yy=1;yy<=bt;yy++){
        memset(dp,-1,sizeof dp);
        ans+=dfs(yy,y+100,yy==bt).s;//对于每个长度为yy的数字进行处理
        ans=(ans+mod)%mod;
    }
    return ans;
}
int main()
{
    base[1]=1;
    for (int i=2;i<=19;i++)
        base[i]=(base[i-1]*10)%mod;
    scanf("%lld%lld%lld",&l,&r,&k);
    {
        printf("%lld",(cal(r,k)-cal(l-1,k)+mod)%mod);
    }
    return 0;
}

AC:第二种:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#define M 1000000007
int k, nod, digit[20];
long long b[20];
struct node
{
	long long n, s;//n是具有相同位数的数的个数,s是n个数的和
}a[20][201];//第一维表示不同的位数,第二维:|k|<=100,所以0=<k+100<=200

node dfs(int d, int e, int flag)
{
	node tmp, t;
	tmp.n = tmp.s = t.n = t.s = 0;
	if(!d)
	{
		if(e == 100)//处理到最后一位,因为最高位符号是从-1开始,且最初位和是从k+100开始,当e=100时,满足
			tmp.n = 1;
		return tmp;
	}
	if(!flag && a[d][e].n != -1)//当位数不是最长位数且处理到该位数时的n不是-1
		return a[d][e];
	int sgn = ((nod - d) % 2 ? 1 : -1);//确定正负号
	int start = (d == nod ? 1 : 0);//当处理位数d为nod时,nod位只能从1开始
	int end = (flag ? digit[d] : 9);//flag为1表示处理到最长位数的数,此时最长位数的最高位只能小于等于该位的值
	for(int i = start; i <= end; i++)
	{
		tmp = dfs(d - 1, e + sgn * i, flag && i == digit[d]);
		if(tmp.n)
		{
			t.n += tmp.n;
			int q = tmp.n % M * i % M * b[d] % M;
			t.s += tmp.s % M;
			t.s %= M;
			t.s += q;
			t.s %= M;
		}
	}
	if(!flag)
		a[d][e] = t;
	return t;
}

int calc(long long num)//计算[1,num]区间所有满足数之和
{
	int cnt = 1, sum = 0;
	while(num)
	{
		digit[cnt++] = num % 10;
		num /= 10;
	}
	for(nod = 1; nod < cnt; nod++)
	{
		memset(a, -1, sizeof(a));
		sum += dfs(nod, k + 100, nod == cnt - 1).s % M;
		sum %= M;
	}
	return sum;
}

int main(void)
{
	long long l, r;
	scanf("%lld%lld%d", &l, &r, &k);
	for(int i = 1; i < 20; i++)
	{
		b[i] = pow(10.0, i - 1);
		b[i] %= M;
	}
	printf("%d\n", (calc(r) - calc(l - 1) + M) % M);

}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值