博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

题解 [51nod1607] 卷积和

题面

解析

神仙LZF随机找出的毒瘤题.

一开始读题过于草率导致naive了.

step 1

看上去特别像数位DP(实际上也有一点).

先预处理出有i位的数(最高位不为0)的数的变换值的和f[i],

它可以通过一段数前后各拼上一个数得到(也就是通过f[i2]转化).

再设g[i]表示最高位可以为0的和,

那么f[i]=(g[i2]90+9045jc[i2]),

$g[i]=g[i-2]100+9045*jc[i-2] $,

(其中jc[i]代表10i不要问为什么叫这个名字)

这里解释一下,

首先中间的一段在前后的0~9每个数字都加了一次,

所以一共加了100次,但由于f的最高位不为0所以是90次.

然后当前后的数字固定后中间的每个排列都要算上所以要乘上10i,

最后前后两端的数的和就是i=09j=09ij+ji,

也就是45452.

step 2

我们可以把题目转化为求i=1i=rf[i]i=1i=l1f[i],

用前缀和的方式把问题分成两个相同的部分.

我们设那一整块为find(x),

xl位,

那前面的l1位的数就直接预处理出来了:i=1l1f[i].

关键就在于处理l位的数.

这里我们拿56789举个例子,

预处理的话就到了9999,

然后枚举每一位的数,

设第i位的数为a[i](从低位到高位依次为1~l).

那么从0(最高位是1)到a[i]1时,后面的数都可以随便取,

在例子里面就分别可以到49999,5999,699,89,8.

然后我们就发现再加上它本身就把所有情况找齐了!

于是我们对每一位进行统计就行了.

step 3

现在我们来考虑怎么统计.

假设我们现在在统计第p位,

枚举每一位j,以及它对应的k=lj+1.

那么对于j(k),它们有三种情况:大于p,等于p,小于p.

当大于p时,它能贡献的就只有它那位上的数字,

其它的要么不能贡献要么在前面已经统计过了.

但是它贡献了0~a[p]1次.

当等于p时,它就贡献了1~a[p]1每个一次.

当小于p时,它就可以取0~9.

**注意,因为p后面的数可以随便取,所以还要在每次计算后面乘上10t,t取决于j,k的位置.

大概就这么多了...(具体的分类写在代码里了).

code:

#include <iostream>
#include <cstdio>
#include <cstring>
#define ll long long
#define fre(x) freopen(x".in","r",stdin),freopen(x".out","w",stdout)
using namespace std;

inline ll read(){
	ll sum=0,f=1;char ch=getchar();
	while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0' && ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
	return f*sum;
}

const int N=101;
const int Mod=1000000007;
ll l,r;
ll f[N],g[N],a[N],jc[N];

inline ll find(ll x){
	int len=0;ll ret=0;
	for(;x;x/=10) a[++len]=x%10;
	for(int i=1;i<len;i++) ret=(ret+f[i])%Mod;
	for(int p=len;p;p--){
		int nm=p-1,st=p==len? 1:0;//最高位不能为0
		int tmp=0,tem=0,tt=a[p]-st;
		for(int i=st;i<a[p];i++) tem+=i,tmp+=i*i;
		//这里说一下因为长度可能为单数,所以有自己乘自己的情况,所以有tmp
		for(int j=len;j;j--){
			int k=len-j+1;
			if(j>p){
				if(k>p) ret=(ret+a[j]*a[k]*jc[nm]*tt)%Mod;//都大于p贡献了a[p]-st次
				else if(k==p) ret=(ret+a[j]*tem*jc[nm])%Mod;//等于p贡献了st~a[p]每个一次
				else ret=(ret+a[j]*45*jc[nm-1]*tt)%Mod;//因为k已经随便取了所以只有nm-1个数了
			}
			else if(j==p){
				if(k>p) ret=(ret+tem*a[k]*jc[nm])%Mod;
				else if(k==p) ret=(ret+tmp*jc[nm])%Mod;
				else ret=(ret+tem*45*jc[nm-1])%Mod;
			}
			else{
				if(k>p) ret=(ret+a[k]*45*jc[nm-1]*tt)%Mod;
				else if(k==p) ret=(ret+45*tem*jc[nm-1])%Mod;
				else{
					if(k==j) ret=(ret+285*jc[nm-1]*tt)%Mod;//自己乘自己
					else ret=(ret+45*45*jc[nm-2]*tt)%Mod;
				}
			}
		}
	}
	for(int i=1;i<=len;i++) ret=(ret+a[i]*a[len-i+1])%Mod;
	return ret;
}

signed main(){
	l=read();r=read();
	f[1]=g[1]=285;jc[0]=1;
	for(int i=1;i<=19;i++) jc[i]=jc[i-1]*10%Mod;
	for(int i=2;i<=19;i++){
		f[i]=(g[i-2]*90%Mod+90*45*jc[i-2]%Mod)%Mod; 
		g[i]=(g[i-2]*100%Mod+90*45*jc[i-2]%Mod)%Mod;
	}
	printf("%lld\n",(find(r)-find(l-1)+Mod)%Mod);
	return 0;
}

posted @   Hastin  阅读(260)  评论(0编辑  收藏  举报
编辑推荐:
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· 展开说说关于C#中ORM框架的用法!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
点击右上角即可分享
微信分享提示