• 题解 [51nod1607] 卷积和


    题面

    解析

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

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

    step 1

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

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

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

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

    那么(f[i]=(g[i-2]*90+90*45*jc[i-2])),

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

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

    这里解释一下,

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

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

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

    最后前后两端的数的和就是(sum_{i=0}^{9}sum_{j=0}^{9}i*j+j*i),

    也就是(45*45*2).

    step 2

    我们可以把题目转化为求(sum_{i=1}^{i=r}f[i]-sum_{i=1}^{i=l-1}f[i],)

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

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

    (x)(l)位,

    那前面的(l-1)位的数就直接预处理出来了:(sum_{i=1}^{l-1}f[i]).

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

    这里我们拿(56789)举个例子,

    预处理的话就到了(9999),

    然后枚举每一位的数,

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

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

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

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

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

    step 3

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

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

    枚举每一位(j),以及它对应的(k=l-j+1).

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

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

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

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

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

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

    **注意,因为(p)后面的数可以随便取,所以还要在每次计算后面乘上(10^t),(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
    ",(find(r)-find(l-1)+Mod)%Mod);
    	return 0;
    }
    
    
  • 相关阅读:
    sprinboot测试类报错问题(Unable to find a @SpringBootConfiguration, you need to use @ContextConfiguration or @SpringBootTest(classes=...))
    实体类的命名细分
    zuul服务网关
    解决aplication.yml没有图标问题
    用户动态权限菜单管理简单实现方式
    servlet+ajax完成select二级联动/动态传值/查询分页功能
    servlet+jsp实现分页功能
    python——Django之admin的使用
    python-Django连接mysql实现可视化会出现的问题
    python——迭代器、列表解析、三元表达式
  • 原文地址:https://www.cnblogs.com/zsq259/p/11425120.html
Copyright © 2020-2023  润新知