这题有一个难点一个坑点,and 你要细点
难点
你要用DP维护出区间内所有和7无关的数的平方和。
这个需要你做一个数学转换。
因为你用DP预处理一个区间的所有与7无关的数,就是通过固定最高位,枚举次高位来递推。所以这个时候我们
可以试着把一个数拆成最高位和剩余位的和,看看它有没有什么特点,从而得到DP的状态转移方程。
所以一个数可以拆成 t∗10k+xi t*10^{k} + x_{i} t∗10k+xi
那么可以固定最高位,枚举低位,得到每个数的平方和如下
(x1+t∗10k)2+(x2+t∗10k)2+⋯(xi+t∗10k)2+⋯+(xn+t∗10k)2 \left ( x_{1} + t*10^{k}\right )^{2} + \left ( x_{2} + t*10^{k}\right )^{2} +\cdots \left ( x_{i} + t*10^{k}\right )^{2} + \cdots + \left ( x_{n} + t*10^{k}\right )^{2} (x1+t∗10k)2+(x2+t∗10k)2+⋯(xi+t∗10k)2+⋯+(xn+t∗10k)2
整理下
n∗(t∗10k)2+2(t∗10k)∗(x1+x2+⋯+xi+⋯+xn)+(x12+x22+⋯+xi2+⋯+xn2)n * (t * 10^{k})^{2} + 2(t * 10^{k}) * (x_{1} + x_{2} +\cdots + x_{i} + \cdots + x_{n}) + (x_{1}^2 + x_{2}^2 + \cdots + x_{i}^2 + \cdots + x_{n}^2)n∗(t∗10k)2+2(t∗10k)∗(x1+x2+⋯+xi+⋯+xn)+(x12+x22+⋯+xi2+⋯+xn2)
再整理下得到
n∗(t∗10k)2+2(t∗10k)∗∑xi+∑xi2n * (t * 10^{k})^{2} + 2(t * 10^{k}) * \sum x_i + \sum x_i^2n∗(t∗10k)2+2(t∗10k)∗∑xi+∑xi2
n:合法低位的的数量 n :合法低位的的数量 n:合法低位的的数量
∑xi:合法低位的和 \sum x_i :合法低位的和∑xi:合法低位的和
∑xi2:合法低位的平方和 \sum x_i^2 :合法低位的平方和∑xi2:合法低位的平方和
所以你要维护上面的3个DP数组。求平方和的时候用下以上公式求就行
坑点
处理DP过程以及求平方和过程需要用到
%7 \% 7 %7
%(109+7) \%(10^{9} + 7 )%(109+7)
但一个数模一个数后再模另一个数答案会有影响
比如 10 % 3 = 1.
但10先%5了 那么10 == 0
0 % 3 = 0
所以一定要提前各自预处理 10^k 对 7 和对 (10 ^9 + 7 )的结果,不要用混了
code
这个代码有auto, 所以信奥里不能编译,HDU用G++可以过
#include <bits/stdc++.h>
using namespace std;
const int N = 22;
typedef long long ll;
struct ty
{
ll s0, s1, s2;// 低位个数,低位和,低位平方和
} f[N][N][7][7];// 处理到第i位,第i位位j,各位和%7 为 l 的数,数本身%7 为 m 的数
ll key9[N];// 10^k % 1e9 + 7
ll key7[N];// 10^k % 7
ll b[N];//存这个数的各个位
const ll mod = 1e9 + 7;
void init()
{
key9[1] = key7[1] = 1;
for (ll i = 2; i < N; ++i)
key9[i] = (key9[i - 1] * 10) % mod, key7[i] = (key7[i - 1] * 10) % 7;
for (ll i = 0; i < 10; ++i)
if (i != 7)
{
f[1][i][i % 7][i % 7].s0 += 1;
f[1][i][i % 7][i % 7].s1 += i;
f[1][i][i % 7][i % 7].s2 += i * i;
}
for (int i = 2; i < N; ++i)// 处理到第i位,第i位位j,各位和%7 为 l 的数,数本身%7 为 m 的数
for (ll j = 0; j < 10; ++j)
{
if (j == 7)//
continue;
for (int l = 0; l < 7; ++l)
for (int m = 0; m < 7; ++m)
for (int k = 0; k < 10; ++k)
{
if (k == 7)
continue;
auto &it1 = f[i][j][ ( l + j ) % 7][( j * key7[i] + m) % 7];//不然数组太长了
auto &it2 = f[i-1][k][l][m];
it1.s0 = ( it1.s0 + it2.s0) % mod;
it1.s1 = ( it1.s1 + it2.s0 * (j * key9[i] % mod ) % mod + it2.s1) % mod;
it1.s2 = ( it1.s2 + j * key9[i] % mod * j % mod * key9[i] % mod * it2.s0 % mod +
key9[i] * j % mod * 2 * it2.s1 % mod + it2.s2) % mod;
}
}
}
ll get(ll i, ll j, ll l, ll m, ll lnum)//用公式求平方和
{
auto &it = f[i][j][l][m];
ll x = (key9[i + 1] * lnum) % mod;// * 运算和 % 运算优先级相同
ll res = ( x * x % mod * it.s0 % mod + x * it.s1 % mod * 2 % mod + it.s2) % mod;
return res;
}
ll solve(ll n)
{
ll k = 0;
while (n)
b[++k] = n % 10, n /= 10;
ll res = 0, i = k;
ll lsum = 0, lnum = 0;
for (; i; --i){
for (ll j = 0; j < b[i]; ++j){
for (ll l = 0; l < 7; ++l)
for (ll m = 0; m < 7; ++m)
{
if (j == 7)
continue;
else if ( ( l + lsum ) % 7 && ( lnum % 7 * key7[i + 1] + m ) % 7 )//特判
res = (res + get(i, j, l, m, lnum % mod)) % mod;
}
}
lsum += b[i];
lnum = lnum * 10 + b[i];
if (b[i] == 7)
break;
}
if (!i && lnum % 7 && lsum % 7)//注意特判这个数本身
res = ( res + lnum % mod * ( lnum % mod ) ) % mod;
return res;
}
int main()
{
init();
ll l, r, T;
scanf("%lld", &T);
while (T--)
{
scanf("%lld %lld", &l, &r);
printf("%lld\n", ( ( solve(r) - solve(l - 1) ) % mod + mod ) % mod);
}
return 0;
}
这篇博客探讨了一个使用动态规划求解区间内所有与7无关的数的平方和的问题。作者详细解释了如何进行数学转换,通过固定最高位并枚举次高位来递推,同时提到了在处理过程中模运算的注意事项。代码实现中包含了预处理步骤和求平方和的公式,并给出了完整的C++代码示例。
981

被折叠的 条评论
为什么被折叠?



