JZOJ 4239. 【五校联考5day2】光棍 (Standard IO)

题意

满足如下条件中任意一个的数\(x\)是合法的:

  • \(x\)\(a\)的倍数。
  • \(x\)在十进制下的数位和是\(a\)的倍数。
  • \(x\)在十进制下某个数位是\(a\)

其中\(a\)给定,\(2\leq a \leq 9\)

现在给出\(l,r,a\),对于\([l,r]\)中每个不合法的\(x\),求\(\sum x^2\)
\(1\leq l \leq r \leq 10^{18},\ 2\leq a \leq 9\)

Solution

这显然是数位dp对吧。。。

但是呢我不会数位dp,至今还没写对过一题,于是今晚恶补了一下,发现也不是很难,套个记忆化搜索的模板就行了。

首先可以转换一下问题,用\([1,r]\)的答案减掉\([1,l-1]\)的答案。
但是题目要求统计不合法数的平方和,我们转换为求合法数的平方和,然后用所有数的平方和减去它(这个可以套公式\(O(1)\)求)。

dfs 的参数是下面几个:

note dfs(int len, int zero, int flag, int mo1, int mo2, int app)
{
}

其中len表示是从低到高第几位,zero表示是否有前导零,flag表示前面几位是不是顶着上界填的,mo1表示当前数对a取模的值,mo2表示当前数的数位和对a取模的值,app(appear)是个0/1状态,表示当前数数位里是否出现了a。

先考虑满足条件的数的个数怎么求,显然套个模板就行了。

再考虑满足条件的数的和怎么求,设后面的数位中某个满足条件的数为\(b\),现在你要填\(a\),后面还有\(k\)位待填,那么填完以后这个数就是\(a*10^k+b\),那么我们只需要知道后面的数位中:
1.满足条件的数的个数
2.满足条件的数的和
就能够求出当前状态满足条件的数的和。

再考虑满足条件的数的平方和怎么求,还是用上面的\(a,b,k\),那么\((a*10^k+b)^2=a^2*10^{2k}+2*a*b*10^k+b^2\)
那也就是知道后面的数位中:
1.满足条件的数的个数
2.满足条件的数的和
3.满足条件的数的平方和
就能够求出当前状态满足条件的数的平方和。

然后这题就解决了。

Code

那个\(166666668\)\(6\)的逆元,因为要做除法。还有\(n\)原本是\(10^{18}\)级别的,如果不先模\(P\)就会爆long long,某gsm因此正解fst了。

#include <cstdio>
#include <cstring>

typedef long long ll;
const ll P = 1e9 + 7;

int T, a, len, arr[20];
ll l, r, pow[40];
struct note { ll s1, s2, s3; } f[20][10][10][2][2];

ll calc(ll n) { n %= P; return 166666668ll * n % P * (n + 1) % P * (2 * n + 1) % P; }

note dfs(int len, int zero, int flag, int mo1, int mo2, int app)
{
    if (!len) return (note){(!mo1 || !mo2 || app), 0, 0};
    if (!flag && ~f[len][mo1][mo2][app][zero].s1) return f[len][mo1][mo2][app][zero];
    note ret = (note){0, 0, 0};
    int mx = flag ? arr[len] : 9;
    for (int i = 0; i <= mx; i++)
    {
        note tmp = dfs(len - 1, zero && !i, flag && i == arr[len], (mo1 * 10 + i) % a, (mo2 + i) % a, app || i == a);
        ret.s1 = (ret.s1 + tmp.s1) % P;
        ret.s2 = (ret.s2 + i * pow[len - 1] % P * tmp.s1 % P + tmp.s2) % P;
        ret.s3 = (ret.s3 + i * i % P * pow[2 * (len - 1)] % P * tmp.s1 + i * pow[len - 1] % P * 2 % P * tmp.s2 % P + tmp.s3) % P;
    }
    if (!flag) f[len][mo1][mo2][app][zero] = ret;
    return ret;
}

ll solve(ll n)
{
    memset(f, -1, sizeof(f));
    len = 0;
    ll tmp = calc(n);
    while (n) arr[++len] = n % 10, n /= 10;
    note ans = dfs(len, 1, 1, 0, 0, 0);
    return (tmp - ans.s3 + P) % P;
}

int main()
{
    pow[0] = 1; for (int i = 1; i <= 38; i++) pow[i] = 1ll * pow[i - 1] * 10 % P;
    scanf("%d", &T);
    while (T--)
    {
        scanf("%lld%lld%d", &l, &r, &a);
        printf("%lld\n", (solve(r) - solve(l - 1) + P) % P);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/zjlcnblogs/p/10336229.html
今日推荐