【HNOI2016】大数

题面

题解

首先考虑如何判断一个区间内的数是否为一个数的倍数。

\(x_i\)表示区间\([i, n]\)组成的数。

如果\([l, r]\)内的数是质数\(p\)的质数,则:

\[\frac{x_l - x_{r + 1}}{10 ^ {r - l + 1}} \equiv 0 \mod p \]

\(p \neq 2, 5\)时,\(\gcd(10, p) = 1\)当且仅当

\[x_l - x_{r + 1} \equiv 0 \mod p \]

\[\Leftrightarrow x_l \equiv x_{r + 1} \mod p \]

时,区间\([l, r]\)内的数是\(p\)的倍数。

\(p = 2\)\(p = 5\)时,可以只通过最后一位有关,很好判断。

于是这两种情况都可以直接莫队。

注意权值要离散化。

代码

#include<cstdio>
#include<cstring>
#include<cctype>
#include<cmath>
#include<algorithm>
#define RG register
#define clear(x, y) memset(x, y, sizeof(x))

const int maxn(100010);
struct qry { int l, r, blk, id; } q[maxn];
long long ans, Ans[maxn]; char s[maxn];
int val[maxn], a[maxn], Pow[maxn], num[maxn];
int n, m, p, SIZE, L, R, cnt, tot;

inline bool cmp(const qry &x, const qry &y)
{
	return x.blk == y.blk ? (x.blk & 1 ? x.r > y.r : x.r < y.r)
		: x.blk < y.blk;
}

inline void insert(int x) { ans += num[val[x]], ++num[val[x]]; }
inline void erase(int x) { --num[val[x]], ans -= num[val[x]]; }
inline void insl(int x) { cnt += (s[x] - '0') % p == 0, ans += cnt; }
inline void dell(int x) { ans -= cnt, cnt -= (s[x] - '0') % p == 0; }
inline void insr(int x) { if((s[x] - '0') % p == 0) ++cnt, ans += R - L + 1; }
inline void delr(int x) { if((s[x] - '0') % p == 0) ans -= R - L + 1, --cnt; }

int main()
{
	scanf("%d%s%d", &p, s + 1, &m); n = strlen(s + 1); Pow[0] = 1;
	for(RG int i = 1; i <= n; i++) Pow[i] = 10ll * Pow[i - 1] % p;
	for(RG int i = n; i; i--) a[i] = val[i] = (val[i + 1] +
			1ll * (s[i] - '0') * Pow[n - i]) % p;
	std::sort(a + 1, a + n + 2); int _n = std::unique(a + 1, a + n + 2) - a - 1;
	for(RG int i = 1; i <= n; i++)
		val[i] = std::lower_bound(a + 1, a + _n + 1, val[i]) - a - 1;
	SIZE = ceil(sqrt(n));
	for(RG int i = 1; i <= m; i++)
		scanf("%d%d", &q[i].l, &q[i].r),
		q[i].blk = q[i].l / SIZE, q[i].id = i;
	std::sort(q + 1, q + m + 1, cmp); L = 1, R = 0;
	if(p != 2 && p != 5) for(RG int i = 1; i <= m; i++)
	{
		++q[i].r;
		while(L > q[i].l) --L, insert(L);
		while(R < q[i].r) ++R, insert(R);
		while(L < q[i].l) erase(L), ++L;
		while(R > q[i].r) erase(R), --R;
		Ans[q[i].id] = ans;
	}
	else for(RG int i = 1; i <= m; i++)
	{
		while(L > q[i].l) --L, insl(L);
		while(R < q[i].r) ++R, insr(R);
		while(L < q[i].l) dell(L), ++L;
		while(R > q[i].r) delr(R), --R;
		Ans[q[i].id] = ans;
	}
	for(RG int i = 1; i <= m; i++) printf("%lld\n", Ans[i]);
	return 0;
}
posted @ 2019-02-26 21:04  xgzc  阅读(148)  评论(0编辑  收藏  举报