题目链接:https://cn.vjudge.net/problem/HDU-4507
单身!
依然单身!
吉哥依然单身!
DS级码农吉哥依然单身!
所以,他生平最恨情人节,不管是214还是77,他都讨厌!
吉哥观察了214和77这两个数,发现:
2+1+4=7
7+7=7*2
77=7*11
最终,他发现原来这一切归根到底都是因为和7有关!所以,他现在甚至讨厌一切和7有关的数!
什么样的数和7有关呢?
如果一个整数符合下面3个条件之一,那么我们就说这个整数和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了,因为后面记录了之后前面的数是不一样的,所以得到的结果不一样,所以我们可以想到计算每一位的贡献。
对于(i + j) ^ 2 = i * i + 2 * i * j + j * j,j可以看成后面的整体,对于 j^2还可以继续dfs计算
对于每个数可以拆分,比如:123 = 1 * 100 + 2 * 10 + 3 * 1
所以我们就可以把后面符合的数的数量记录下来num, sum计算后面符合数的和,用于计算2 * i * j,ans就是记录结果 j * j
now.num = (now.num + tmp.num) % mod;
now.sum = (now.sum + i * f[pos] % mod * tmp.num % mod + tmp.sum) % mod;
now.ans = (now.ans + tmp.ans + 2 * cnt * tmp.sum % mod + cnt * cnt % mod * tmp.num % mod) % mod;
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
struct node {
ll num;
ll sum;
ll ans;
node(ll num_ = 0, ll sum_ = 0, ll ans_ = 0) {
num = num_; sum = sum_; ans = ans_;
}
}dp[20][10][10];
int w[20], len;
ll f[22];
node dfs(int pos, int limit, int sum, int num) {
if(pos < 0) {
if(sum && num ) return node(1, 0, 0);
return node(0, 0, 0);
}
if(!limit && dp[pos][sum][num].ans != -1) return dp[pos][sum][num];
int p = limit ? w[pos] : 9;
ll cnt = 0;
node tmp, now;
for(int i = 0; i <= p; i++) {
if(i == 7) continue;
cnt = f[pos] * i % mod;
tmp = dfs(pos - 1, limit && i == p, (sum + i) % 7, (num * 10 + i) % 7);
now.num = (now.num + tmp.num) % mod;
now.sum = (now.sum + cnt % mod * tmp.num % mod + tmp.sum) % mod;
now.ans = (now.ans + tmp.ans + 2 * cnt * tmp.sum % mod + cnt * cnt % mod * tmp.num % mod) % mod;
}
if(!limit) dp[pos][sum][num] = now;
return now;
}
ll solve(ll x) {
if(x == 0) return 0;
len = 0;
while(x) {
w[len++] = x % 10;
x /= 10;
}
return dfs(len - 1, 1, 0, 0).ans;
}
int main() {
f[0] = 1;
for(int i = 1; i <= 18; i++)
f[i] = f[i - 1] * 10 % mod;
ll l, r;
int T;
scanf("%d", &T);
for(int i = 0; i < 20; i++)
for(int j = 0; j < 10; j++)
for(int k = 0; k < 10; k++)
dp[i][j][k].ans = -1;
while(T--) {
scanf("%lld %lld", &l, &r);
printf("%lld\n", ((solve(r) - solve(l - 1)) % mod + mod) % mod);
}
return 0;
}
附上一下傻逼的第一思路:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
ll dp[20][10][10];
int w[20], len;
ll dfs(int pos, int limit, int sum, int num, ll cnt) {
if(pos < 0) {
if(sum % 7 == 0 || num % 7 == 0) return 0;
return cnt * cnt % mod;
}
if(!limit && dp[pos][sum][num] != -1) return dp[pos][sum][num];
int p = limit ? w[pos] : 9;
ll res = 0;
for(int i = 0; i <= p; i++) {
if(i == 7) continue;
res += dfs(pos - 1, limit && i == p, (sum + i) % 7, (num * 10 + i) % 7, (cnt * 10 + i) % mod);
res %= mod;
}
if(!limit) dp[pos][sum][num] = res;
return res;
}
ll solve(ll x) {
if(x == 0) return 0;
len = 0;
while(x) {
w[len++] = x % 10;
x /= 10;
}
return dfs(len - 1, 1, 0, 0, 0);
}
int main() {
ll l, r;
int T;
scanf("%d", &T);
memset(dp, -1, sizeof(dp));
while(T--) {
scanf("%lld %lld", &l, &r);
printf("%lld\n", ((solve(r) - solve(l - 1)) % mod + mod) % mod);
}
return 0;
}