1 条题解
-
0
自动搬运
来自洛谷,原作者为

Kagamine_Rin
主页:tiy7zto3|互关原则:只要私信就会用小号回关,一定要发,列表看不到的|可恶洛谷把我7√→6√|灰名可能会暂时取关,若被杀请私信(话说谁JC我,封面怎么是wssb,既然只是一个挂名账号那就不改了)搬运于
2025-08-24 23:16:11,当前版本为作者最后更新于2025-05-21 19:44:37,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
这题真的是毒瘤啊……
注:本题解提供 的解法,不是正解,如果要学习 的正解请看别的题解。
简要题意
给定一个数组 ,有 次询问(),并回答 中有多少个 满足 且 。
强制在线。
看到 就可以想到使用根号分治。
考虑将值域作为下标,存储该数值在 中的位置:
std::vector<int> vtop[V]; for (int i = 1; i <= n; ++ i) cin >> a[i]; for (int i = 1; i <= n; ++ i) vtop[a[i]].emplace_back(i);设阈值为 (一般另 , 为值域)。
当模数 时,最多只要枚举 个可能的 满足条件,所以直接暴力。我们可以枚举每一个 ,在 的所有可能的下标中用二分找到左端点()和右端点(),答案即为 ,代码如下:
if (p > B) { for (int i = (L - x + p - 1) / p * p + x; i <= R; i += p) ans += (upper_bound(vtop[i].begin(), vtop[i].end(), r) - vtop[i].begin()) - (lower_bound(vtop[i].begin(), vtop[i].end(), l) - vtop[i].begin()); // 这里没有 +1 是因为 upper_bound 会找到 rpos 右边一格,所以 upper_bound 这里返回的是 rpos + 1 cout << ans << "\n"; }当模数 时,我们可以对下标进行分块。创建一个
vector数组 预处理出 ( 和 分别表示该块的左端点和右端点)中满足 的所有 组成的序列,代码如下:for (int i = 1; i <= n; ++ i) { const int bl_id = (i - 1) / Block_N + 1; // 该下标对应的块编号 for (int p = 1; p <= Block_V; ++ p) d[bl_id][p][a[i] % p].emplace_back(a[i]); }然后不能忘记排序:
for (int i = 0; i <= bl(n); ++ i) for (int p = 0; p <= Block_V; ++ p) for (int x = 0; x <= Block_V; ++ x) sort(d[i][p][x].begin(), d[i][p][x].end());在被询问时就很显然了:
else { // if (p <= B) if (bl(l) == bl(r)) { // 左端点和右端点在同一个块,特殊处理 for (int i = l; i <= r; ++ i) ans += L <= a[i] && a[i] <= R && a[i] % p == x; cout << ans << "\n"; } else { while ((l - 1) % Block_N) L <= a[l] && a[l] <= R && a[l] % p == x && ++ ans, ++ l; while (r % Block_N) L <= a[r] && a[r] <= R && a[r] % p == x && ++ ans, -- r; // ↑不是完整的块,暴力处理 for (int i = bl(l); i <= bl(r); ++ i) ans += (upper_bound(d[i][p][x].begin(), d[i][p][x].end(), R) - d[i][p][x].begin()) - (lower_bound(d[i][p][x].begin(), d[i][p][x].end(), L) - d[i][p][x].begin()); // 完整的块,直接相加(没有 +1 的原因和上面同理) cout << ans << "\n"; } }然后我们终于写完了这份代码,并获得了 10 分的高分!
%:import "functional" %:import "algorithm" %:import "iostream" %:import "string.h" %:import "queue" %:define For(a, b, c) for (int a = b, a##END = c; a <= a##END; ++ a) %:define bl(x) (((x) - 1) / Block_N + 1) %:define bl_l(b) ((b) * Block_N - Block_N + 1) %:define bl_r(b) ((b) * Block_N) const int N = 1e5 + 7, V = 2e5 + 7, Block_V = 100, Block_N = 316; // Block_V 就是上文写道的 B,这里用于和 Block_N 进行区分 int a[N]; std::vector<int> vtop[V]; std::vector<int> d[318][Block_V + 1][Block_V + 1]; main() { using namespace std; ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int n, type, lastans = 0; cin >> n >> type; for (int i = 1; i <= n; ++ i) { cin >> a[i], vtop[a[i]].emplace_back(i); const int bl_id = (i - 1) / Block_N + 1; for (int p = 1; p <= Block_V; ++ p) d[bl_id][p][a[i] % p].emplace_back(a[i]); } for (int i = 0; i <= Block_N; ++ i) for (int p = 0; p <= Block_V; ++ p) for (int x = 0; x <= Block_V; ++ x) sort(d[i][p][x].begin(), d[i][p][x].end()); int T; cin >> T; while (T --) { int l, r, L, R, p, x, ans = 0; cin >> l >> r >> L >> R >> p >> x; if (type) l ^= lastans, r ^= lastans, L ^= lastans, R ^= lastans, p ^= lastans, x ^= lastans; if (p > Block_V) for (int i = (L - x + p - 1) / p * p + x; i <= R; i += p) ans += (upper_bound(vtop[i].begin(), vtop[i].end(), r) - vtop[i].begin()) - (lower_bound(vtop[i].begin(), vtop[i].end(), l) - vtop[i].begin()); else { if (bl(l) == bl(r)) { for (int i = l; i <= r; ++ i) ans += L <= a[i] && a[i] <= R && a[i] % p == x;//, fprintf(stderr, "%d, ", a[i] % p); goto opt; } while ((l - 1) % Block_N) ans += L <= a[l] && a[l] <= R && a[l] % p == x, ++ l; while (r % Block_N) ans += L <= a[r] && a[r] <= R && a[r] % p == x, -- r; for (int i = bl(l), iEND = bl(r); i <= iEND; ++ i) ans += (upper_bound(d[i][p][x].begin(), d[i][p][x].end(), R) - d[i][p][x].begin()) - (lower_bound(d[i][p][x].begin(), d[i][p][x].end(), L) - d[i][p][x].begin()); } opt: cout << (lastans = ans) << "\n"; } }为什么?
因为这道题时间和空间卡得很紧,连正解都要卡常,更何况非正解呢?
以下是我们需要注意的地方:
- 由于 的部分常数比较大,所以不能使用传统的 作为阈值,而要使用更小的数,比如 100。
- 询问区间( 和 )不一定是从 的,事实上可能长度远小于 ,所以我们还可以加上判断
if (p > B || (R - ((L - x + p - 1) / p * p + x)) <= 32768)。 - 当
d[i][j][k].size() <= 1时,根本不需要排序,排序就是浪费时间,所以我们要在排序前写上:if (d[i][j][k].size() > 1)。 - 对于
for (int p = 1; p <= B; ++ p)这里,由于 ,我们完全可以手写 100 遍代替一个for循环。
经过无数次的尝试,我们终于得到了以下 AC 代码,并愉快地切掉了一道黑题(Record):
#import "algorithm" // 码风过丑,勿喷 #import "iostream" #import "vector" #define For(a, b, c) for (int a = b, a##END = c; a <= a##END; ++ a) #define bl(x) (((x) - 1) / Block_N + 1) #define bl_l(b) ((b) * Block_N - Block_N + 1) #define bl_r(b) ((b) * Block_N) const int N = 1e5 + 7, V = 2e5 + 7, Block_V = 100, Block_N = 750; int a[N]; std::vector<int> vtop[V]; std::vector<int> d[bl(100000) + 2][Block_V + 1][Block_V + 1]; main() { using namespace std; ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int n, type; cin >> n >> type; for (int i = 1; i <= n; ++ i) cin >> a[i]; for (int i = 1; i <= n; ++ i) vtop[a[i]].emplace_back(i); for (int i = 1; i <= n; ++ i) { const int bl_id = (i - 1) / Block_N + 1; #define op(p) d[bl_id][p][a[i] % p].emplace_back(a[i]) op(1), op(2), op(3), op(4), op(5), op(6), op(7), op(8), op(9), op(10), op(11), op(12), op(13), op(14), op(15), op(16), op(17), op(18), op(19), op(20), op(21), op(22), op(23), op(24), op(25), op(26), op(27), op(28), op(29), op(30), op(31), op(32), op(33), op(34), op(35), op(36), op(37), op(38), op(39), op(40), op(41), op(42), op(43), op(44), op(45), op(46), op(47), op(48), op(49), op(50), op(51), op(52), op(53), op(54), op(55), op(56), op(57), op(58), op(59), op(60), op(61), op(62), op(63), op(64), op(65), op(66), op(67), op(68), op(69), op(70), op(71), op(72), op(73), op(74), op(75), op(76), op(77), op(78), op(79), op(80), op(81), op(82), op(83), op(84), op(85), op(86), op(87), op(88), op(89), op(90), op(91), op(92), op(93), op(94), op(95), op(96), op(97), op(98), op(99), op(100); } for (int i = 0; i <= bl(n); ++ i) for (int p = 0; p <= Block_V; ++ p) for (int x = 0; x <= Block_V; ++ x) if (d[i][p][x].size() > 1) sort(d[i][p][x].begin(), d[i][p][x].end()); int T, ans = 0; cin >> T; while (T --) { int l, r, L, R, p, x; cin >> l >> r >> L >> R >> p >> x; type && (l ^= ans, r ^= ans, L ^= ans, R ^= ans, p ^= ans, x ^= ans), ans = 0; if (p > Block_V || (R - ((L - x + p - 1) / p * p + x)) <= 32768) { for (int i = (L - x + p - 1) / p * p + x; i <= R; i += p) ans += (upper_bound(vtop[i].begin(), vtop[i].end(), r) - vtop[i].begin()) - (lower_bound(vtop[i].begin(), vtop[i].end(), l) - vtop[i].begin()); cout << ans << "\n"; } else { if (bl(l) == bl(r)) { for (int i = l; i <= r; ++ i) ans += L <= a[i] && a[i] <= R && a[i] % p == x;//, fprintf(stderr, "%d, ", a[i] % p); cout << ans << "\n"; } else { while ((l - 1) % Block_N) L <= a[l] && a[l] <= R && a[l] % p == x && ++ ans, ++ l; while (r % Block_N) L <= a[r] && a[r] <= R && a[r] % p == x && ++ ans, -- r; for (int i = bl(l), iEND = bl(r); i <= iEND; ++ i) ans += (upper_bound(d[i][p][x].begin(), d[i][p][x].end(), R) - d[i][p][x].begin()) - (lower_bound(d[i][p][x].begin(), d[i][p][x].end(), L) - d[i][p][x].begin()); cout << ans << "\n"; } } } }卡常不易,点个赞再走吧~对了,请不要抄题解,因为你抄了也过不了,就是浪费时间(别问我怎么知道的)
- 1
信息
- ID
- 11702
- 时间
- 1500ms
- 内存
- 1024MiB
- 难度
- 7
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者