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

题意

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

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

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

现在给出(l,r,a),对于([l,r])中每个不合法的(x),求(sum x^2)
(1leq l leq r leq 10^{18}, 2leq 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
", (solve(r) - solve(l - 1) + P) % P);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/zjlcnblogs/p/10336229.html