51nod 1607-卷积和(数位DP)

基准时间限制:1 秒 空间限制:131072 KB 分值: 40  难度:4级算法题
 收藏
 关注

杰西最近在研究卷积,例如一个数字1234,那么经过杰西的变换后,就会变成1*4+2*3+3*2+4*1,例如一个数字是234,那么就会变成2*4+3*3+4*2。

形象化的,我们可以设f(x)表示x经过杰西变换后的值。用公式表示  x=dn1dn2...d2d1d0  那么  f(x)=n1i=0didn1i  

例如f(1234)=20,f(234)=25。

现在他想到了一个问题。

如果他想知道对于所有i=L~R时的f(i)的值的和,该怎么做呢。

现在这个问题交给了你。

但是这个答案可能会非常大,因此杰西只想知道最终答案对1000000007取模后的答案。


Input
单组测试数据
一行两个整数L,R(1<=L<=R<=10^18)。
Output
一个数表示答案。
Input示例
3 233
Output示例
8730
System Message  (题目提供者)
C++的运行时限为:1000 ms ,空间限制为:131072 KB  示例及语言说明请按这里

 允许其他 AC 的用户查看此代码,分享代码才能查看别人的代码并有机会获得勋章


感觉是一道比较烦的数位DP,情况比较多,看了题解看了很久才看明白23333,然后自己对着码了一遍,算是理解透彻了。。。比较难写,代码中加了一些注释。。。


#include<map>   
#include<stack>          
#include<queue>          
#include<vector>          
#include<math.h>          
#include<stdio.h>          
#include<iostream>          
#include<string.h>          
#include<stdlib.h>  
#include<algorithm> 
#include<functional>  
using namespace std;          
typedef long long  ll;         
#define inf  1000000000     
#define mod 1000000007           
#define maxn  1000005
#define lowbit(x) (x&-x)          
#define eps 1e-9
ll digit[25],q[25];
void init()
{
	q[1]=1;
	for(int i=2;i<=20;i++)
		q[i]=q[i-1]*10%mod;
}
ll dp(ll x,int a,int b,int v)
{
	ll res=0;
	int i,j,len=a+b-1;
	if(v==0)
	{
		if(a==1)
			return q[a+b-2]*2025%mod;
		else
			return q[a+b-3]*9*2025%mod;
	}
	else//类似奇数位求贡献的方法,注意细节
	{
		for(i=len;i>b;i--)
			res=(res+(digit[i]-(i==len))*2025*q[i-2]%mod)%mod;
		for(i=1;i<digit[b];i++)
			res=(res+q[b-1]*45*i%mod)%mod;
		for(i=b-1;i>a;i--)
			res=(res+digit[i]*q[i-1]*digit[b]*45%mod)%mod;
		for(i=1;i<digit[a];i++)
			res=(res+q[a]*digit[b]*i%mod)%mod;
		res=(res+(x%q[a]+1)*digit[a]*digit[b]%mod)%mod;
	}
	return res;
}
ll solve(ll x)
{
	if(x<1) return 0;
	int i,j,len=0;
	ll tmp=x,res=0;
	while(tmp)
	{
		digit[++len]=tmp%10;
		tmp/=10;
	}
	for(i=1;i<=len;i++)
	{
		if(2*i-1>len)
			break;
		if(2*i-1<len)//位数小于上限时中间位的总贡献
		{
			if(i==1)
				res=(res+285)%mod;
			else
				res=(res+q[2*i-2]*9*285%mod)%mod;
		}
		if(2*i-1==len)
		{
			for(j=len;j>i;j--)//计算比中间位高的位数上的数发生变化时的贡献
				res=(res+(digit[j]-(j==len))*285*q[j-1]%mod)%mod;
			for(j=1;j<digit[i];j++)//高位不变,中间位变化时的贡献
				res=(res+q[i]*j*j%mod)%mod;
			res=(res+digit[i]*digit[i]*(x%q[i]+1)%mod)%mod;//仅低位变化时的贡献
		}
	}
	for(i=1;i<=len;i++)//计算成对卷积和
		for(j=i+1;j<=len;j++)
		{
			if(i+j-1>len)
				break;
			res=(res+2*dp(x,i,j,i+j-1==len)%mod)%mod;
		}
		return res;
}
int main(void)
{
	init();
	ll l,r;
	while(scanf("%lld%lld",&l,&r)!=EOF)
	{
		printf("%lld\n",(solve(r)-solve(l-1)+mod)%mod);
	}
	return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值