2021杭电多校第一场
06. Xor sum
07. Pass!
09. KD-Graph
10. zoto
题意:给出一个序列,离线查询m个区间$[l_i,r_i]$中,在数值区间$[d_i,u_i]$中有多少个不同的数,n=1e5
首先考虑在数值区间上维护问题,也就是维护一个支持单点修改和区间查询的数据结构,很容易想到用线段树之类的方法实现$O(\log n)$的修改和查询
离线查询若干个区间,可以再想到莫队算法,每次修改复杂度$O(\sqrt n)$,查询$O(1)$,将上述两种结构合并后发现,修改复杂度达到了$O(\sqrt n\log n)$,显然超时
于是为了使总复杂度限制在$O(n\sqrt n)$,需要在数值区间上维护一个$O(1)$修改,$O(\sqrt n)$查询的做法,这个方法就是分块,维护$num[i]$记录数i出现的次数,$sum[i]$记录第i个块中不同数的个数,两者合并起来刚好是$O(n\sqrt n)$
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
const int N = 1e5 + 5;
int T, n, m, a[N], block[N], bs, ans[N], sq, num[N], sum[N];
int block2[N], sq2;
struct Query {
int l, r, d, u, idx;
bool operator < (Query &Q) const {
if (block[l] != block[Q.l])
return l < Q.l;
else return r < Q.r;
}
}q[N];
void modify(int pos, int type) {
int x = a[pos];
if (type == 1) {
num[x]++;
if (num[x] == 1) sum[block2[x]]++;
} else {
num[x]--;
if (num[x] == 0) sum[block2[x]]--;
}
}
int Ask(int l, int r) {
int res = 0;
if (block2[l] == block2[r]) {
for (int i = l; i <= r; i++) res += (num[i] > 0);
return res;
}
for (int i = block2[l] + 1; i <= block2[r] - 1; i++) {
res += sum[i];
}
for (int i = l; i <= sq2 * block2[l]; i++) res += (num[i] > 0);
for (int i = sq2 * (block2[r] - 1) + 1; i <= r; i++) res += (num[i] > 0);
return res;
}
int main() {
scanf("%d", &T);
sq2 = (int)sqrt(N);
for (int i = 0; i <= 100000; i++) block2[i] = i <= sq2 ? 1 : block2[i - sq2] + 1;
while(T--) {
memset(num, 0, sizeof(num));
memset(sum, 0, sizeof(sum));
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
int sq = (int)sqrt(n);
for (int i = 1; i <= n; i++) block[i] = i <= sq ? 1 : block[i - sq] + 1;
for (int i = 1; i <= m; i++) {
int x[2], y[2]; scanf("%d %d %d %d", &x[0], &y[0], &x[1], &y[1]);
q[i] = {x[0], x[1], y[0], y[1], i};
}
sort(q + 1, q + 1 + m);
int L, R;
L = 1, R = 0;
for (int i = 1; i <= m; i++) {
while(R < q[i].r) modify(++R, 1);
while(L > q[i].l) modify(--L, 1);
while(R > q[i].r) modify(R--, -1);
while(L < q[i].l) modify(L++, -1);
ans[q[i].idx] = Ask(q[i].d, q[i].u);
}
for (int i = 1; i <= m; i++) {
printf("%d\n", ans[i]);
}
}
return 0;
}