/* |
2013腾讯马拉松初赛第1场 |
1003 吉哥系列故事——恨7不成妻 |
|
Time Limit: 0.5 Seconds Memory Limit: 65536K |
|
单身! |
依然单身! |
吉哥依然单身! |
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 |
*/ |
/* |
解题报告: |
这题的数据范围很大,如果直接模拟的话肯定超时,还有这题时限好像还是500MS的,故意搞不这样就是不让一般的暴力方法过这题。 |
这题和以前不一样的地方在他要求的是平方和,而不是和。以前求和还是好做的。 |
一般这种题目的话肯定是数位DP了。 |
第一种想法,上面有三个条件,我们可以先用上面的条件求出至少符合一个条件的数字平方和。这个要用一下容斥原理。比较麻烦。 |
我是直接把7这一位数字去掉,然后再加两维状态保存当前数字对7取余的结果,和每一位之和对7取余的结果。 |
下面说一下我的做法。 |
dp[i][j][p][q]表示长度为i的以j为首位的数字每一位数字之和对7取余是p,整个数字对7取余是q的平方和。 |
现问题的关键是如何通过dp[i-1][k][a][b]推出来dp[i][j][p][q] |
我们可以观察,假设dp[i-1][k][a][b]里面的数字是 |
n1,n2,...n[T] dp[i-1][k][a][b]这个状态下有T个数字是合法的 |
那么dp[i][j][p][q]状态下的数字就是 |
j*ten[i-1]+n1,j*ten[i-1]+n2,...j*ten[i-1]+n[T] |
ten[i]代表10的i次方 |
他们的平方和就是 |
(j*ten[i-1]+n1)^2+(j*ten[i-1]+n2)^2+...+(j*ten[i-1]+n[T])^2 |
展开一下 |
T*(j*ten[i-1])^2+2*j*ten[i-1]*(n1+n2+...+n[T])+n1^2+n2^2+n3^2+...+n[T]^2 |
从上面的式子可以看出,要计算出平方和,还需要统计一下合法数字的个数,还有,这些合法数字的总和。 |
所以我们要分三步DP。 |
dp复杂度是O(20*10*10*7*7),对于50个数据还是可以接受的。 |
来自:chenwenwen0210 的百度空间 |
*/ |
#include<stdio.h> |
#include<time.h> |
#include<algorithm> |
#include<string.h> |
using namespace std; |
typedef __int64 lld; |
const int MAX = 510000; |
const int INF = 1000000000; |
const lld MOD = 1000000007; |
lld dp[20][10][7][7] = {0}; |
lld cnt[20][10][7][7] = {0}; |
lld sum[20][10][7][7] = {0}; |
lld multi[10][10]; |
lld add[10][10]; |
lld ten[20]; |
lld tp[20]; |
void init() { |
lld i, j, p, q, k; |
lld a, b; |
cnt[0][0][0][0] = 1; |
//计算个数 |
for (i = 1; i < 20; i++) { |
for (j = 0; j < 10; j++) { |
if (j == 7) continue ; |
for (a = 0; a < 7; a++) { |
for (b = 0; b < 7; b++) { |
if (cnt[i - 1][j][a][b] == 0) continue ; |
for (k = 0; k < 10; k++) { |
if (k == 7) continue ; |
p = add[k][a]; //加起来对7取余 |
q = multi[k][ten[i - 1]]; //乘起来对7取余 |
q = add[q][b]; |
cnt[i][k][p][q] += cnt[i - 1][j][a][b]; |
if (cnt[i][k][p][q] >= MOD)cnt[i][k][p][q] -= MOD; |
} |
} |
} |
} |
} |
//计算和 |
for (i = 1; i < 20; i++) { |
for (j = 0; j < 10; j++) { |
if (j == 7) continue ; |
for (a = 0; a < 7; a++) { |
for (b = 0; b < 7; b++) { |
for (k = 0; k < 10; k++) { |
if (k == 7) continue ; |
p = add[k][a]; |
q = multi[k][ten[i - 1]]; |
q = add[q][b]; |
lld z = k * tp[i - 1] % MOD; |
sum[i][k][p][q] += cnt[i - 1][j][a][b] * z + sum[i - 1][j][a][b]; |
if (sum[i][k][p][q] >= MOD)sum[i][k][p][q] %= MOD; |
} |
} |
} |
} |
} |
//计算平方和 |
for (i = 1; i < 20; i++) { |
for (j = 0; j < 10; j++) { |
if (j == 7) continue ; |
for (a = 0; a < 7; a++) { |
for (b = 0; b < 7; b++) { |
for (k = 0; k < 10; k++) { |
if (k == 7) continue ; |
p = add[k][a]; |
q = multi[k][ten[i - 1]]; |
q = add[q][b]; |
lld z = k * tp[i - 1] % MOD; |
dp[i][k][p][q] += cnt[i - 1][j][a][b] * z % MOD * z + dp[i - 1][j][a][b] + 2 * z * sum[i - 1][j][a][b]; |
if (dp[i][k][p][q] >= MOD)dp[i][k][p][q] %= MOD; |
} |
} |
} |
} |
} |
} |
lld split(lld bit[], lld n) { |
int i = 0; |
for (i = 0; n; i++) { |
bit[i] = n % 10; |
n /= 10; |
} |
return i; |
} |
bool ok(lld n) { |
if (n % 7 == 0) return false ; |
lld sum = 0; |
while (n) { |
if (n % 10 == 7) return false ; |
sum += n % 10; |
n /= 10; |
} |
if (sum % 7 == 0) return false ; |
return true ; |
} |
lld calc(lld n) { |
if (n == 0) return 0; |
lld bit[22], len; |
lld ret = 0; |
lld i, j, k, p, q, a, b; |
len = split(bit, n); |
if (ok(n))ret += n % MOD * (n % MOD) % MOD; |
for (i = 1; i < len; i++) { |
for (j = 1; j < 10; j++) { |
if (j == 7) continue ; |
for (p = 1; p < 7; p++) { |
for (q = 1; q < 7; q++) { |
ret += dp[i][j][p][q]; |
if (ret >= MOD)ret -= MOD; |
} |
} |
} |
} |
lld presum = 0; //前面各位和对7取余 |
lld premod7 = 0; //前面组成数字对7取余 |
lld pre = 0; //前面组成数字对MOD取余 |
for (i = len; i >= 1; i--) { |
j = 0; |
if (i == len)j++; |
for (; j < bit[i - 1]; j++) { |
if (j == 7) continue ; |
for (k = 0; k < 10; k++) { |
if (k == 7) continue ; |
for (a = 0; a < 7; a++) { |
for (b = 0; b < 7; b++) { |
p = add[presum][j]; |
p = add[p][a]; |
if (p == 0) continue ; |
q = multi[premod7][10 - 7]; |
q = add[q][j]; |
q = multi[q][ten[i - 1]]; |
q = add[q][b]; |
if (q == 0) continue ; |
lld z = (pre * 10 + j) * tp[i - 1] % MOD; |
ret += z * z % MOD * cnt[i - 1][k][a][b] + dp[i - 1][k][a][b] + 2 * z * sum[i - 1][k][a][b]; |
ret %= MOD; |
} |
} |
} |
} |
if (bit[i - 1] == 7) break ; |
pre = (pre * 10 + bit[i - 1]) % MOD; |
presum = add[presum][bit[i - 1]]; |
premod7 = multi[premod7][10 - 7]; |
premod7 = add[premod7][bit[i - 1]]; |
} |
return ret; |
} |
int main() { |
lld n, m; |
int i, j; |
int T = 0; |
for (i = 0; i < 10; i++) { |
for (j = 0; j < 10; j++) { |
add[i][j] = (i + j) % 7; |
multi[i][j] = i * j % 7; |
} |
} |
ten[0] = 1; |
tp[0] = 1; |
for (i = 1; i < 20; i++) { |
ten[i] = ten[i - 1] * 10 % 7; |
tp[i] = tp[i - 1] * 10 % MOD; |
} |
init(); |
scanf ( "%d" , &T); |
while (T--) { |
scanf ( "%I64d%I64d" , &n, &m); |
lld ans = 0; |
ans += calc(m); |
ans -= calc(n - 1); |
if (ans < 0)ans += MOD; |
printf ( "%I64d\n" , ans); |
} |
return 0; |
} |
by: 发表于:2017-08-11 10:09:17 顶(0) | 踩(0) 回复
??
回复评论