【数位dp】恨7不成妻

【题目描述】
单身!
依然单身!
吉哥依然单身!
DS 级码农吉哥依然单身!
所以,他平生最恨情人节,不管是 214 还是 77 ,他都讨厌!
吉哥观察了 214 和 77 这两个数,发现:

2+1+4=7

7+7=7×2

77=7×11

最终,他发现原来这一切归根到底都是因为和 7 有关!所以,他现在甚至讨厌一切和 7 有关的数!

什么样的数和 7 有关呢?如果一个整数符合下面三个条件之一,那么我们就说这个整数和 7 有关:

整数中某一位是 7 ;

整数的每一位加起来的和是 7 的整数倍;

这个整数是 7 的整数倍。

现在问题来了:吉哥想知道在一定区间内和 7 无关的数字的平方和。

【输入】
输入数据的第一行是测试数据组数 T ,然后接下来的 T 行表示 T 组测试数据。

每组数据在一行内包含两个正整数 L,R

【输出】
对于每组数据,请计算 [L,R] 中和 7 无关的数字的平方和,并将结果对 109+7 取模后输出。

【样例输入 】
3
1 9
10 11
17 17
【样例输出】
236
221
0
提示
对于全部数据,1≤T≤50,1≤L≤R≤1018

【思路】

显然是数位dp的题。
限制条件都是很朴素的,常规操作解决。
那么这道题的问题在于我们需要维护平方和。
那么对于当前的第p位,平方和:
q s u m = ( a i + b 1 0 p 1 ) qsum=\sum(ai+b*10^{p-1})
其中ai表示p-1位搜到的每个合法的数。
那么拆开我们可以得到:
q s u m = a i 2 + ( b 1 0 p 1 ) 2 + 2 ( b 1 0 p 1 ) ( a i ) qsum=\sum ai^{2}+\sum (b*10^{p-1})^{2}+2*(b*10^{p-1})*\sum(ai)
对于当前状态,b的10的幂显然已知。
对于第一项,我们直接维护平方和;
对于第二项,我们需要维护ai的个数;
对于第三项,我们需要维护ai的和;
因此问题就解决了。
代码:

#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<algorithm>
#include<queue>
#define re register
#define LL long long
using namespace std;
LL n,m,a,b,c;
LL mod=1e9+7;
struct node{
	LL sum;
	LL qsum;
	LL num;
}f[20][7][7];
//位数 数位之和 原数mod7的余数
LL q[10001],len=0;
LL lo[10001];
node dfs(LL pos,LL sum1,LL sum2,bool lim)
{
	if(!pos)
	{
		node p;
		p.num=(sum1!=0 && sum2!=0);
		p.sum=p.qsum=0;
		return p;
	}
	if(!lim && f[pos][sum1][sum2].num!=-1)return f[pos][sum1][sum2];
	LL up=lim?q[pos]:9;
	LL sum=0,qsum=0,num=0;
	for(LL re i=0;i<=up;i++)
	{
		if(i==7)continue;
		node ans=dfs(pos-1,(sum1+i)%7,(sum2*10+i)%7,lim&i==up);
		sum+=ans.sum+(i*(lo[pos-1]%mod)*ans.num);
		sum%=mod;
		num+=ans.num;
		num%=mod;
		qsum+=ans.qsum;
		qsum%=mod;
		qsum+=(((((ans.num*i%mod)*i)%mod)*lo[pos-1]%mod)*(lo[pos-1]%mod))%mod+2*(((lo[pos-1]*i)%mod)*ans.sum)%mod;
		qsum%=mod;
	}
	if(!lim)f[pos][sum1][sum2]=(node){sum,qsum,num};
	return (node){sum,qsum,num};
} 
LL ask(LL x)
{
	len=0;
	while(x)
	{
		q[++len]=x%10;
		x/=10;
	}
	return dfs(len,0,0,1).qsum;
}
int main()
{
	lo[0]=1;memset(f,-1,sizeof(f));
	for(LL re i=1;i<=18;i++)lo[i]=lo[i-1]*10%mod;
	int t;scanf("%d",&t);
	while(t--)
	{
		scanf("%lld%lld",&a,&b);
		printf("%lld\n",(((ask(b)-ask(a-1))%mod)+mod)%mod);
	}
}

猜你喜欢

转载自blog.csdn.net/weixin_44111457/article/details/88595449