题意:
如果一个整数符合下面3个条件之一,那么我们就说这个整数和7有关——
1、整数中某一位是7;
2、整数的每一位加起来的和是7的整数倍;
3、这个整数是7的整数倍;
现在问题来了:吉哥想知道在一定区间内和7无关的数字的平方和。
1、整数中某一位是7;
2、整数的每一位加起来的和是7的整数倍;
3、这个整数是7的整数倍;
现在问题来了:吉哥想知道在一定区间内和7无关的数字的平方和。
Input
输入数据的第一行是case数T(1 <= T <= 50),然后接下来的T行表示T个case;每个case在一行内包含两个正整数L, R(1 <= L <= R <= 10^18)。
Output
请计算[L,R]中和7无关的数字的平方和,并将结果对10^9 + 7 求模后输出。
Sample Input
3 1 9 10 11 17 17
Sample Output
236 221 0
思路:
好题啊!以为数位dp只能计数,没想到还能搞平方和,虽然也是由基本的计数推导的。
这道题如果只问这样的数的个数cnt,那就很好想了,设计状态dp[pos][pre_sum][pre_mod]表示的是不受limit影响的,位数为pos,到目前的数位和%7为pre_sum,构建到当前位的总数%7为pre_mod,这样的数中合法的数的个数。那么套用数位dp模板就可以解决。
但是这里要求的是平方和。所以就来了个有趣的套路。
我们利用一个结构体node {cnt, sum, sqsum}表示当前位置所计算的合法数的个数,总和,以及他们的平方和。
假设res是dfs递归中的上一层结果,当前位数选择的是i,然后递归求得下一层的结果是tmp,那么就有几个递推式可以利用。
显然,res.cnt = 所有 tmp.cnt的和;
假设当前pos位选择的是位数是i,然后接着递归pos-1位到0位得到的数是x,那么加上pos位这个数y = i * (10^pos) + x,res.sum就是所有y的和,tmp.sum就是所有x的和,等式两边同时求和,不难发现 i * (10^pos)这一项就要出现tmp.cnt次,所以,res.sum = i * (10^pos) * tmp.cnt + tmp.sum;
同理求解平方和,x与y的定义如上,对于平方和可以有这样的关系 y^2 = (i * (10^pos) +x)^2,拆开来看,就是y^2 = (i*(10^pos))^2 + 2*(i*(10^pos))*x + x^2,同样等式左右两边求和,y^2求和就是res.sqsum,x^2求和就是tmp.sqsum,(i*(10^pos))^2对于每个x都要出现一次,所以这一项需要出现tmp.cnt次,所以整理可得,res.sqsum = (i*(10^pos))^2*tmp.cnt + 2 * (i*(10^pos))^2 * tmp.sum + tmp.sqsum
理解清楚思路后代码很简单,注意模运算。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
struct node {
ll cnt, sum, sqsum;
}dp[20][10][10];
ll ten[20];
int a[20];
void init() {
ten[0] = 1;
for (int i = 1; i < 20; i++)
ten[i] = ten[i - 1] * 10 % mod;
memset(dp, -1, sizeof(dp));
}
node dfs(int pos, int pre_sum, int pre_mod, bool limit) {
if (pos == -1) {
if (pre_sum == 0 || pre_mod == 0) return (node){0, 0, 0};
return (node){1, 0, 0};
}
if (!limit && dp[pos][pre_sum][pre_mod].cnt != -1) return dp[pos][pre_sum][pre_mod];
int up = limit ? a[pos] : 9;
node res = (node){0, 0, 0};
for (int i = 0; i <= up; i++) {
if (i == 7) continue;
node tmp = dfs(pos - 1, (pre_sum + i) % 7, (pre_mod * 10 + i) % 7, limit && a[pos] == i);
res.cnt += tmp.cnt;
res.cnt %= mod;
res.sum += (tmp.sum + ((ten[pos] * i) % mod) * tmp.cnt % mod) % mod;
res.sum %= mod;
res.sqsum += (tmp.sqsum + ((2 * ten[pos] * i) % mod) * tmp.sum) % mod;
res.sqsum %= mod;
res.sqsum += (tmp.cnt * ten[pos]) % mod * ten[pos] % mod * i * i % mod;
res.sqsum %= mod;
}
if (!limit) dp[pos][pre_sum][pre_mod] = res;
return res;
}
ll solve (ll x) {
int pos = 0;
while (x) {
a[pos++] = x % 10;
x /= 10;
}
return dfs(pos - 1, 0, 0, true).sqsum;
}
int main() {
init();
int T;
scanf("%d", &T);
while (T--) {
ll n, m;
scanf("%I64d%I64d", &n, &m);
printf("%I64d\n", ((solve(m) - solve(n - 1)) % mod + mod) % mod);
}
return 0;
}