求 [L,R] 中不满足以下任意一条的数的平方和
1 整数中某一位是7
2 整数的每一位加起来的和是7的整数倍
3 这个整数是7的整数倍;
一眼看去又是一个数位dp,但是怎么统计平方和呢?
假设我们在统计最高位为x的n个数的平方和
有 ∑i=1n(x+a)2=n×x2+2x×∑i=1nai+∑i=1na2i
其中和x相关的贡献是前两项。也就是说,我们统计能接在当前前缀后面的后缀的个数和后缀的和,就可以计算出x对整个平方和的贡献。
怎么统计后缀和呢?
∑i=1n(x+ai)=n×x+∑i=1nai
也需要统计后缀的个数。
那么照着这个统计下去就好了。
因为一开始写的时候没有想太多就强行写了三遍dfs。。。
有空重写一遍
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int maxn = 20,mod = 1e9 + 7;
int num[maxn];
LL ten[maxn * 2];
LL cnted[maxn][2][7][7];
LL count(int pos,bool bnd,int left,int sumer){
if(pos < 0) return left != 0 && sumer != 0;
LL & ncnt = cnted[pos][bnd][left][sumer];
if(~ncnt) return ncnt;
ncnt = 0;
int bound = bnd ? num[pos] : 9;
for(int i=0;i<=bound;i++){
if(i == 7) continue;
(ncnt += count(pos-1
,bnd && bound == i
,(left * 10 + i ) % 7
,(sumer + i ) % 7 ) ) %= mod;
}
return ncnt;
}
LL sumed[maxn][2][7][7];
LL sum(int pos,bool bnd,int left,int sumer){
if(pos < 0) return 0;
LL & nsum = sumed[pos][bnd][left][sumer];
if(~nsum) return nsum;
nsum = 0;
int bound = bnd ? num[pos] : 9;
for(int i=0;i<=bound;i++){
if(i == 7) continue;
(nsum += sum (pos-1
,bnd && bound == i
,(left * 10 + i ) % 7
,(sumer + i ) % 7 ) ) %= mod;
(nsum += (count(pos-1
,bnd && bound == i
,(left * 10 + i ) % 7
,(sumer + i ) % 7 )
* i
* ten[pos]) % mod ) %= mod;
}
return nsum;
}
LL dp[maxn][2][7][7];
LL dfs(int pos,bool bnd,int left,int sumer){
if(pos < 0) return 0;
LL & ndp = dp[pos][bnd][left][sumer];
if(~ndp) return ndp;
ndp = 0;
int bound = bnd ? num[pos] : 9;
for(int i=0;i<=bound;i++){
if(i == 7) continue;
(ndp += dfs(pos-1
,bnd && bound == i
,(left * 10 + i ) % 7
,(sumer+ i ) % 7 ) ) %= mod;
(ndp += ((count(pos-1
,bnd && bound == i
,(left * 10 + i ) % 7
,(sumer+ i ) % 7 )
* i * i ) % mod
* ten[pos * 2]) % mod ) %= mod;
(ndp += ((sum(pos-1
,bnd && bound == i
,(left * 10 + i ) % 7
,(sumer+ i ) % 7 )
* i * 2 ) % mod
* ten[pos]) % mod ) %= mod;
}
return ndp;
}
LL cal(LL x){
int len = 0;
while(x){
num[len++] = x % 10;
x /= 10;
}
memset(cnted,-1,sizeof(cnted));
memset(sumed,-1,sizeof(sumed));
memset(dp,-1,sizeof(dp));
return dfs(len-1,true,0,0);
}
int main(){
ten[0] = 1;
for(int i=1;i<maxn * 2;i++) ten[i] = (ten[i-1] * 10 ) % mod;
int T;
scanf("%d",&T);
LL l,r;
while(T-- && ~scanf("%I64d %I64d",&l,&r)){
printf("%I64d\n",(cal(r) - cal(l-1) + mod) % mod);
}
return 0;
}
重写之后果然短了不少
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int maxn = 20,mod = 1e9 + 7;
LL dp[maxn][2][7][7];
LL sum[maxn][2][7][7];
LL cnt[maxn][2][7][7];
bool vis[maxn][2][7][7];
int num[maxn];
LL ten[maxn * 2];
LL cnter(int pos,bool bnd,int lef,int mor,int nex){
lef = (lef * 10 + nex) % 7;
mor = (mor + nex) % 7;
if(pos == 0)
return mor != 0 && lef != 0;
return cnt[pos-1][bnd && nex == num[pos]][lef][mor];
}
LL sumer(int pos,bool bnd,int lef,int mor,int nex){
if(pos == 0) return 0;
lef = (lef * 10 + nex) % 7;
mor = (mor + nex) % 7;
return sum[pos-1][bnd && nex == num[pos]][lef][mor];
}
#define nexter pos,bnd,lef,mor,i
LL dfs(int pos,bool bnd,int lef,int mor){
if(pos < 0) return 0;
if(vis[pos][bnd][lef][mor])
return dp[pos][bnd][lef][mor];
LL & ndp = dp [pos][bnd][lef][mor];
LL & ncnt = cnt[pos][bnd][lef][mor];
LL & nsum = sum[pos][bnd][lef][mor];
ndp = ncnt = nsum = 0;
int bound = bnd ? num[pos] : 9;
for(int i=0;i<=bound;i++){
if(i == 7) continue;
(ndp += dfs(pos-1,bnd && i == bound,(lef*10+i)%7,(mor+i)%7)) %= mod;
(ncnt += cnter(nexter) ) %= mod;
(nsum += sumer(nexter) ) %= mod;
(nsum += ((cnter(nexter) * i ) % mod * ten[pos] ) % mod ) %= mod;
(ndp += ((cnter(nexter) * i * i ) % mod * ten[pos * 2]) % mod ) %= mod;
(ndp += ((sumer(nexter) * i * 2 ) % mod * ten[pos] ) % mod ) %= mod;
}
vis[pos][bnd][lef][mor] = true;
return ndp;
}
LL cal(LL n){
int len = 0;
while(n){
num[len++] = n % 10;
n /= 10;
}
memset(vis,0,sizeof(vis));
return dfs(len-1,true,0,0);
}
int main(){
ten[0] = 1;
for(int i=1;i<maxn * 2;i++)
ten[i] = (ten[i-1] * 10) % mod;
int T;
scanf("%d",&T);
LL l,r;
while(T-- && ~scanf("%I64d %I64d",&l,&r)){
printf("%I64d\n",(cal(r) - cal(l-1) + mod ) % mod);
}
return 0;
}