数数

这一道题给我们最大的启示就是一定要学会固定数字!下面要求的数组,如果按照数位DP的套路,从小到大考虑数位以及数位上的数字,是很容易想到需要求这些数组的

Pow[i]=Bi,f[i]=k=0iBk

h[i]表示所有i位数字的所有前缀子串的和

比如123456一共有6位,他的所有前缀子串为1,12,123,1234,12345,123456,他的所有前缀子串和就是这六个数加起来,那么h[6]就表示000000,000001,,999999所有这些数字的前缀子串和的和

我们求h[i]时,考虑最高位填什么。此时最高位在变换(可以从0一直到B1),而剩下的数字也在变换(可以从00...0i-1个0一直到99...9i-1个9),我们就不好讨论,所以先固定假设我们已经选择了最高位的数字,这个数字为j,剩下位的数字为abc...

那么此时的所以前缀为j,ja,jab...,他们分别可以表示成j,jPow[1]+a,jPow[2]+aPow[1]+b...,所以这一个已经固定的数字的所有前缀和就是jf[i1]+(abc...这个数字的所有前缀子串和)

此时我们再假设j固定,abc...在变化,再求一个和(即当最高位为j时,对h[i]的贡献),则为jf[i1]Pow[i1]+h[i1](注意一共有Pow[i1]个数)

然后我们再假设j也在变换,再求一个和,则有h[i]=((j=0B1j)f[i1]Pow[i1])+Bh[i1]

以上过程完全可以先固定abc...,假设j在变化,然后假设abc...在变化,答案是一模一样的

g[i]表示所有i位数字的所有子串的和,用以上同样的方法可以得出g[i]=Bg[i1]+h[i]

然后试填的过程见以下代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+10;
const ll p=20130427;
int n,a[N];
ll f[N],g[N],h[N],Pow[N],B;
void prework()
{
	f[0]=1;
	Pow[0]=1;
	for(int i=1;i<=N-10;i++)
	{
		Pow[i]=Pow[i-1]*B%p;
		f[i]=(f[i-1]+Pow[i])%p;
		h[i]=(((B-1)*B>>1)%p*f[i-1]%p*Pow[i-1]%p+B*h[i-1]%p)%p;
		g[i]=(B*g[i-1]%p+h[i])%p;
	}
}
ll work()
{
	ll sum=0,val=0,res=0;
	//sum表示已经填了的数字的所有子串的和
	//val表示已经填了的数字的所有前缀子串的和
	//rse表示结果 
	for(int i=n;i>=2;i--) 
	for(int j=1;j<B;j++)
	{
		res=(res+g[n-i]+h[n-i])%p;
		res=(res+(j*Pow[n-i]%p*f[n-i]%p))%p;
	}
	//这个循环是在填写只有1位,2位...i-1位的情况
	//注意不能直接填写最高位的0
	//因为此时会导致重复计算
	//比如n=3,数字为095
	//那么本来应该只算9,5,95这三个子串的
	//但如果直接从最高位的0开始算
	//就会算成0,9,5,09,95,095
	//显然重复了 
	for(int i=1;i<a[1];i++)
	{
		res=(res+g[n-1])%p;
		res=(res+(i*Pow[n-1]%p*f[n-1]%p+h[n-1])%p)%p;
	}
	//考虑最高位的写法 
	val=a[1],sum=a[1];
	for(int i=2;i<=n;i++)
	{
		for(int j=0;j<a[i];j++)
		{
			res=(res+g[n-i])%p;
			ll temp=(val*B%p+(ll)j*i%p)%p;
			res=(res+(temp*Pow[n-i]%p*f[n-i]%p+i*h[n-i])%p)%p;
			res=(res+sum*Pow[n-i]%p)%p;
		}
		val=(val*B%p+(ll)a[i]*i%p)%p;
		sum=(sum+val)%p;
	}
	//可以用博客讲的方法推一下上面代码的式子 
	return (res+sum)%p;
	//注意还要在加上sum
	//因为要计算这个数本身的贡献 
}
int main()
{
	scanf("%lld",&B);
	prework();
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	int j=n;
	while(!a[j]) 
	{
		a[j]=B-1;
		j--;
	}
	a[j]--;
	if(j==1&&!a[j]) 
	{
		n--;
		for(int i=1;i<=n;i++) a[i]=B-1;
	}
	ll res1=work();
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	ll res2=work();
	printf("%lld",(res2-res1+p)%p);
    return 0;
}

以上代码肯定会超时,但是这个优化太简单了,具体见洛谷的提交

update 2024.9.17

重新做一遍,做出来了

其实我们走起来应该想的是求g这个数组,枚举最高位数字,我们就要去统计以j开头的所有子串的和;当然其实不一定想得到h这个数组,我们考虑统计长度刚好为l的所有B进制数的和,不难得到为Bl(Bl1)2;然后考虑j后面的数字的长度为k(也就是现在我们在统计以j开头长度为k+1的前缀的和),再对所有k求和之后,可以得到

B×h[i1]=Bi((Bi11)BB1i+1)2

但是注意,这道题目的模数不是质数,搞这个搞了好久。。纯诈骗,快速幂的幂的值就不是p2了而是ϕ(p)1

posted @   最爱丁珰  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
历史上的今天:
2021-10-31 证明
点击右上角即可分享
微信分享提示