【luogu P6466】分散层叠算法(Fractional Cascading)(二分)(模板)
分散层叠算法(Fractional Cascading)
题目链接:luogu P6466
题目大意
给你 k 个长度为 n 的有序数组,然后要你在线查询:
给你一个数 x,求每个数组 x 的非严格后继的异或和。
思路
首先考虑一些暴力之类的?
不难想到 \(O(nk\log n)\) 的二分暴力。
然后你考虑一个神奇的方法:
弄一个二维数组 \(B\),然后第 \(i\) 维除了放第 \(i\) 个有序数组,你还可以放第 \(i+1\) 维的数组中偶数位的部分。(这个可以归并排序实现)
这样有什么用呢?你会发现你如果求这个数组二分的答案,它原来的有序数组的答案其实可以用这个的答案在二分得到(而且其实就没有必要二分了,直接找后面第一个是原来有序数组的数,可以在归并排序的时候得到)。
然后你要求 \(i+1\) 的数组的答案的时候,你会发现你又像上面一样搞,位置的相差竟然至多是 \(1\)!
然后你考虑你 \(B\) 数组每一维的数量,考虑每个的贡献,当前的是 \(1\),隔一个是 \(\dfrac{1}{2}\),然后 \(\dfrac{1}{4}\),所以总的来讲不到 \(2\)。
然后你就可以 \(O(nk+q\log n+qk)\) 做了。
然后可以小小扩展一下,如果给定是一个图然后每个点有一个数列,度数不超过一个值 \(d\),每次询问是求图上一条链的每个点的答案。
那我们这里就是隔 \(d\) 个选一个,然后预处理的部分就是乘上一个 \(\log d\)。
它其实有一个类似均摊的感觉,所以你可以和分块之类的结合起来,但是我就不会啦awa。
(不过你可以参考 IOI 2020 中国国家候选队论文《浅谈利用分散层叠算法对经典分块问题的优化》)
代码
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 10000 + 10;
const int K = 100 + 10;
const int Q = 500000 + 10;
struct node {
int x, nxt1, nxt2;
}b[K][N << 1];
int n, k, q, d, lstans, sz[K], a[K][N], fir[N << 1], x;
int main() {
scanf("%d %d %d %d", &n, &k, &q, &d);
for (int i = 1; i <= k; i++)
for (int j = 1; j <= n; j++) scanf("%d", &a[i][j]);
sz[k] = n; for (int i = 1; i <= n; i++) b[k][i] = (node){a[k][i], i, 0};
for (int i = k - 1; i >= 1; i--) {
int x = 1, y = 2, nxt1 = 0, nxt2 = 0;
while (x <= n && y <= sz[i + 1]) {
if (a[i][x] < b[i + 1][y].x) nxt1 = x, b[i][++sz[i]] = (node){a[i][x], nxt1, nxt2 + 1}, x++;
else nxt2 = y, b[i][++sz[i]] = (node){b[i + 1][y].x, nxt1 + 1, nxt2}, y = y + 2;
}
while (x <= n) nxt1 = x, b[i][++sz[i]] = (node){a[i][x], nxt1, nxt2 + 1}, x++;
while (y <= sz[i + 1]) nxt2 = y, b[i][++sz[i]] = (node){b[i + 1][y].x, nxt1 + 1, nxt2}, y = y + 2;
}
for (int i = 1; i <= sz[1]; i++) fir[i] = b[1][i].x;
for (int t = 1; t <= q; t++) {
scanf("%d", &x); x ^= lstans;
int p = lower_bound(fir + 1, fir + sz[1] + 1, x) - fir;
lstans = 0;
for (int i = 1; i <= k; i++) {
while (p <= sz[i] && b[i][p].x < x) p++;
while (p >= 2 && b[i][p - 1].x >= x) p--;
if (p <= sz[i]) {
lstans ^= a[i][b[i][p].nxt1];
p = b[i][p].nxt2;
}
else p = sz[i + 1] + 1;
}
if (t % d == 0) {
printf("%d\n", lstans);
}
}
return 0;
}