测试地址:Seven
做法: 本题需要用到数位DP。
是的,我看这题题目名字实在太长,就自己给取了个名字…首先看它给的条件,如果只求数字个数的话,非常明显是数位DP的形式,只需要设
n
u
m
(
i
,
j
,
k
)
num(i,j,k)
num(i,j,k)为前
i
i
i位中,不卡上界的,数位和对
7
7
7的余数为
j
j
j,数对
7
7
7的余数为
k
k
k的数字的个数,很容易就能转移。而要求数字的和较难一点,但我们也能较快地想出转移。而现在要求数字的平方和,这就有点复杂了。
然而实际上也不很复杂,你只需要一个结论:一些数的平方和,等于这些数和的平方,减去这些数两两间的乘积之和乘
2
2
2。这个结论也很容易理解,因为
(
∑
a
i
)
2
=
∑
(
a
i
∑
a
j
)
=
∑
a
i
2
+
∑
i
≠
j
a
i
⋅
a
j
(\sum a_i)^2=\sum (a_i\sum a_j)=\sum a_i^2+\sum_{i\ne j} a_i\cdot a_j
(∑ai)2=∑(ai∑aj)=∑ai2+∑i̸=jai⋅aj,移项即可得到上面的结论。这样一来,我们需要求
f
(
i
,
j
,
k
)
f(i,j,k)
f(i,j,k),表示满足对应条件(和上面状态定义一样,就不重复写了)的数的和,还有
g
(
i
,
j
,
k
)
g(i,j,k)
g(i,j,k),表示满足对应条件的数两两之间乘积的和。
我们发现所有转移都可以转化为以下问题:我们有两个集合,分别求出了对应的
f
,
g
f,g
f,g和
n
u
m
num
num,它们合并之后的
f
,
g
f,g
f,g和
n
u
m
num
num怎么求?这个比较好推,就自己推一下就行了,实在不懂就看我代码。还有一个问题是,我们有一个集合,求出了对应的
f
,
g
f,g
f,g和
n
u
m
num
num,那么在这些集合的数之后添加一位数
x
x
x后,集合的
f
,
g
f,g
f,g和
n
u
m
num
num怎么变化?这个就稍微复杂一些,以三个数的集合
S
=
{
a
,
b
,
c
}
S=\{a,b,c\}
S={a,b,c}为例,令新加的位为
x
x
x,则:
(
10
a
+
x
)
+
(
10
b
+
x
)
+
(
10
c
+
x
)
=
10
(
a
+
b
+
c
)
+
3
x
(10a+x)+(10b+x)+(10c+x)=10(a+b+c)+3x
(10a+x)+(10b+x)+(10c+x)=10(a+b+c)+3x
(
10
a
+
x
)
(
10
b
+
x
)
+
(
10
b
+
x
)
(
10
c
+
x
)
+
(
10
a
+
x
)
(
10
c
+
x
)
=
100
(
a
b
+
b
c
+
a
c
)
+
20
(
a
+
b
+
c
)
x
+
3
x
2
(10a+x)(10b+x)+(10b+x)(10c+x)+(10a+x)(10c+x)=100(ab+bc+ac)+20(a+b+c)x+3x^2
(10a+x)(10b+x)+(10b+x)(10c+x)+(10a+x)(10c+x)=100(ab+bc+ac)+20(a+b+c)x+3x2
根据这两个式子我们可以找到规律,首先
n
u
m
num
num不变,
f
f
f就等于原来的
f
f
f乘
10
10
10加上
x
x
x乘
n
u
m
num
num,而
g
g
g等于原来的
g
g
g乘
100
100
100,加上
10
(
n
u
m
−
1
)
⋅
10(num-1)\cdot
10(num−1)⋅原来的
f
f
f,最后加上
n
u
m
(
n
u
m
−
1
)
2
⋅
x
2
\frac{num(num-1)}{2}\cdot x^2
2num(num−1)⋅x2。
有了这些东西,就可以解决转移的问题了,像模板数位DP一样做就行了。
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1000000007;
const ll inv2=500000004;
int T,n;
ll s[25],num[25][7][7],f[25][7][7],g[25][7][7];
void update(int dgt,int j,int k,ll nowf,ll nowg,ll nownum)
{
g[dgt][j][k]=(g[dgt][j][k]+nowg+nowf*f[dgt][j][k])%mod;
f[dgt][j][k]=(f[dgt][j][k]+nowf)%mod;
num[dgt][j][k]=(num[dgt][j][k]+nownum)%mod;
}
ll solve()
{
int nowi=0,nowj=0,nowk=0;
ll nowsum=0;
memset(f[n+1],0,sizeof(f[n+1]));
memset(g[n+1],0,sizeof(g[n+1]));
memset(num[n+1],0,sizeof(num[n+1]));
for(int dgt=n;dgt>=1;dgt--)
{
memset(f[dgt],0,sizeof(f[dgt]));
memset(g[dgt],0,sizeof(g[dgt]));
memset(num[dgt],0,sizeof(num[dgt]));
for(int j=0;j<7;j++)
for(int k=0;k<7;k++)
for(int now=0;now<=9;now++)
{
if (now==7) continue;
int newj=(j+now)%7,newk=(k*10ll+now)%7;
ll nowf=(f[dgt+1][j][k]*10ll%mod+now*num[dgt+1][j][k]%mod)%mod;
ll nowg=g[dgt+1][j][k]*100ll%mod;
nowg=(nowg+f[dgt+1][j][k]*now%mod*10ll%mod*(num[dgt+1][j][k]-1ll+mod)%mod)%mod;
nowg=(nowg+num[dgt+1][j][k]*(num[dgt+1][j][k]-1ll)%mod*inv2%mod*now*now%mod)%mod;
ll nownum=num[dgt+1][j][k];
update(dgt,newj,newk,nowf,nowg,nownum);
}
if (dgt<n&&(!nowi))
{
for(int now=0;now<s[dgt];now++)
{
if (now==7) continue;
int newj=(nowj+now)%7,newk=(nowk*10ll+now)%7;
update(dgt,newj,newk,(nowsum*10ll+now)%mod,0ll,1ll);
}
}
nowi=(nowi||(s[dgt]==7));
nowj=(nowj+s[dgt])%7;
nowk=(nowk*10ll+s[dgt])%7;
nowsum=(nowsum*10ll+s[dgt])%mod;
for(int now=1;now<=((dgt==n)?(s[dgt]-1):9);now++)
{
if (now==7) continue;
update(dgt,now%7,now%7,now,0ll,1ll);
}
}
f[0][0][0]=g[0][0][0]=num[0][0][0]=0;
for(int j=1;j<7;j++)
for(int k=1;k<7;k++)
update(0,0,0,f[1][j][k],g[1][j][k],num[1][j][k]);
ll ans=((f[0][0][0]*f[0][0][0]%mod-g[0][0][0]*2ll)%mod+mod)%mod;
if ((!nowi)&&nowj&&nowk) ans=(ans+nowsum*nowsum)%mod;
return ans;
}
int main()
{
scanf("%d",&T);
while(T--)
{
ll ans=0,x;
scanf("%lld",&x);
x--;
if (x)
{
n=0;
while(x)
{
s[++n]=x%10;
x/=10;
}
ans=(mod-solve())%mod;
}
scanf("%lld",&x);
n=0;
while(x)
{
s[++n]=x%10;
x/=10;
}
ans=(ans+solve())%mod;
printf("%lld\n",ans);
}
return 0;
}