吉哥系列故事――恨7不成妻 HDU - 4507 (数位dp)
题目链接:https://cn.vjudge.net/contest/163023#problem/I
题目大意:
如果一个整数符合下面3个条件之一,那么我们就说这个整数和7有关
1、整数中某一位是7;
2、整数的每一位加起来的和是7的整数倍;
3、这个整数是7的整数倍;
现在问题来了:吉哥想知道在一定区间内和7 无关 的数字的平方和。也就是三个条件都不满足呗。
题目分析:
这个题….
首先这三个条件都是基础数位dp,就不说了。
重点是怎么去求平方和。
需要维护三个值
1.符合条件数的个数 cnt
2.符合条件数的和 sum
3.符合条件数的平方和 sqr
为什么要维护这三个呢,接着往下看你就知道了
假定dfs推出返回的结构体是tmp,当前结果的结构体是ans
其中1是基础的数位dp很好维护;
2 tmp.sum * 10 + (10 ^ pos * i) * ans.cnt 就是上一步状态的和加上这一步加的
这一步加的就是10的当前位次方乘以i,因为有ans.cnt个嘛,所以再乘以ans.cnt
3 首先重新构建一下这个数 (10^pos * i + x)x是这个数的后面部分,就是上一次状态得到的那个数,则平方和就是(10^len*i)^2+x^2+2*10^len*i*x, 其中x^2=tmp.sqr;
ans.sqr += (2*10^pos*i*x)*tmp.cnt=(2*10^pos*i)*next.sum(神奇的化简)
ans.sqr += (10^pos*i)^2*tmp.cnt;
#include <bits/stdc++.h>
typedef long long ll;
const ll MOD = 1e9 + 7;
ll p[25];
ll data[20];
struct node
{
ll cnt, sum, sqr;
node(){cnt = -1; sum = sqr = 0;}
node(ll cnt, ll sum, ll sqr) : cnt(cnt), sum(sum), sqr(sqr) {}
}dp[20][10][10];
node dfs(int pos, int sum1, int sum2, bool limit)
{
if(pos == 0)
{
if(sum1 && sum2)
{
return node(1, 0, 0);
}
return node(0, 0, 0);
}
if(!limit && dp[pos][sum1][sum2].cnt != -1) return dp[pos][sum1][sum2];
int up = limit ? data[pos] : 9;
node ans;
ans.cnt = 0;
for(int i = 0; i <= up; i++)
{
if(i == 7) continue;
node tmp = dfs(pos - 1, (i + sum1) % 7, (sum2 * 10 + i) % 7, i == up && limit);
ans.cnt += tmp.cnt;
ans.cnt %= MOD;
ans.sum += (tmp.sum + ((p[pos] * i) % MOD) * tmp.cnt % MOD) % MOD;
ans.sum %= MOD;
ans.sqr += (tmp.sqr + (( 2 * p[pos] * i) % MOD) * tmp.sum) % MOD;
ans.sqr %= MOD;
ans.sqr += ((tmp.cnt * p[pos]) % MOD * p[pos] % MOD * i * i % MOD);
ans.sqr %= MOD;
}
if(!limit) dp[pos][sum1][sum2] = ans;
return ans;
}
ll solve(ll n)
{
int pos = 0;
while(n)
{
data[++pos] = n % 10;
n /= 10;
}
node v = dfs(pos, 0, 0, true);
return v.sqr;
}
int main()
{
int t;
scanf("%d", &t);
p[1] = 1;
for(int i = 2; i <= 20; i++) p[i] = (p[i - 1] * 10) % MOD;
while(t--)
{
ll l, r;
scanf("%lld %lld", &l, &r);
ll ans = solve(r);
ans -= solve(l - 1);
printf("%lld\n", (ans % MOD + MOD) % MOD);
}
}