分析:对于前30%的数据直接暴力模拟即可,对于另外30%的数据,因为每次的p是一样的,所以可以用莫队来维护,先离散化一下,再用一个桶统计次数.
100%的做法和之前做过的一道模拟赛题很像,当模数很小的时候分块,否则就暴力修改.其实看到区间操作第一感觉是线段树,但是线段树并不能维护这个,分块维护的信息多一些,所以分块.在模数较小的时候记录一下第i个块,模p等于v的有多少个,即g[i][p][v],利用前缀和统计1~i个块的个数.在模数较大的时候因为只有v,v+p,v+2p对答案有影响,所以记录第i个块值为v的有多少个,即f[i][v],同样也可以用前缀和处理一下.查询的时候还是分模数的大小来进行,先统计完整包含在块里面的,在暴力统计在块外面的就可以了.
当有些信息线段树维护不了的时候可以考虑一下分块,分块的时候可以根据范围来决定什么时候分块,什么时候用其它的方法,前缀和可以加速区间查询操作.
60分暴力:
#include <cstdio> #include <cmath> #include <cstring> #include <iostream> #include <algorithm> using namespace std; int n, m, a[1000010], b[1000010], cnt, tot, c[1000100], sizee, L = 1, R = 0, tong[1000010], ans[1000010]; bool flag = true; struct node { int l, r, p, v,id; }e[100010]; bool cmp(node a, node b) { if (a.l / sizee == b.l / sizee) return a.r < b.r; return a.l < b.l; } void add(int x) { tong[a[x]]++; } void del(int x) { tong[a[x]]--; } int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) scanf("%d", &a[i]); cnt = 0; for (int i = 1; i <= m; i++) { scanf("%d%d%d%d", &e[i].l, &e[i].r, &e[i].p, &e[i].v); e[i].id = i; if (i != 1 && e[i].p != e[i - 1].p) flag = false; b[++cnt] = e[i].v; } if (flag) { for (int i = 1; i <= n; i++) { a[i] %= e[1].p; b[++cnt] = a[i]; } memcpy(c, b, sizeof(b)); sort(c + 1, c + 1 + cnt); tot = unique(c + 1, c + 1 + cnt) - c - 1; for (int i = 1; i <= n; i++) a[i] = lower_bound(c + 1, c + 1 + tot, a[i]) - c - 1; for (int i = 1; i <= m; i++) e[i].v = lower_bound(c + 1, c + 1 + tot, e[i].v) - c - 1; sizee = (int)sqrt(n); sort(e + 1, e + 1 + m, cmp); for (int i = 1; i <= m; i++) { int l = e[i].l, r = e[i].r; while (R < r) add(++R); while (R > r) del(R--); while (L > l) add(--L); while (L < l) del(L++); ans[e[i].id] = tong[e[i].v]; } for (int i = 1; i <= m; i++) printf("%d\n", ans[i]); } else { for (int i = 1; i <= m; i++) { int cnt = 0; for (int j = e[i].l; j <= e[i].r; j++) if (a[j] % e[i].p == e[i].v) cnt++; printf("%d\n", cnt); } } return 0; }
100分正解:
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; int n, m, a[100010], maxn, block, cnt, maxx, l[10010], r[10010], ans; int f[1010][10010], g[1010][70][70], kuai[100100]; int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); maxn = max(maxn, a[i]); } block = 100; cnt = (n - 1) / block + 1; maxx = 60; for (int i = 1; i <= cnt; i++) { l[i] = (i - 1) * block + 1, r[i] = min(n, i * block); for (int j = 0; j <= maxn; j++) f[i][j] = f[i - 1][j]; for (int j = 1; j <= maxx; j++) for (int k = 0; k < maxx; k++) g[i][j][k] = g[i - 1][j][k]; for (int j = l[i]; j <= r[i]; j++) { kuai[j] = i; f[i][a[j]]++; for (int k = 1; k <= maxx; k++) g[i][k][a[j] % k]++; } } for (int i = 1; i <= m; i++) { int ll, rr, p, v; scanf("%d%d%d%d", &ll, &rr, &p, &v); ans = 0; v %= p; int L = kuai[ll], R = kuai[rr]; if (L < R) { if (p <= 60) ans += g[R - 1][p][v] - g[L][p][v]; else for (int i = v; i <= maxn; i += p) ans += f[R - 1][i] - f[L][i]; for (int j = ll; j <= r[L]; j++) if (a[j] % p == v) ans++; for (int j = l[R]; j <= rr; j++) if (a[j] % p == v) ans++; } else for (int j = ll; j <= rr; j++) if (a[j] % p == v) ans++; printf("%d\n", ans); } return 0; }