题目链接 Chef and odd queries
题意 给定$n$个区间和$q$个询问,每个询问给定$m$个点,求这$n$个区间中有多少个包含了$m$个点中的奇数个。
分类操作。
对于$m >$ $\sqrt{n}$的询问直接一个前缀和依次枚举,时间复杂度$O(n)$,因为这样的询问不会超过$\sqrt{n}$个,
所以规定时间内可以通过。
对于$m <$ $\sqrt{n}$的询问,两两枚举这$m$个点,枚举的时间复杂度为$O(n)$
枚举的点对必满足这两个点之间(包括这两个点)点个数为奇数。
当枚举的点对为$(x_{i}, x_{j})$的时候,我们要查询左端点在$[x_{i-1}+1,x_{i}]$,右端点落在$[x_{j},x_{j+1}-1]$的区间个数。
对$x[]$建立主席树,$root[i]$维护的是坐标范围 $<= i$的这些点,那么对于每次查询可以在$O(logn)$的时间完成。
当然也可以通过离线在树状数组上询问。
时间复杂度$O(n^{\frac{3}{2}}logn)$
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) const int N = 1e5 + 10; const int M = 5e6 + 10; struct node{ int l, r; void scan(){ scanf("%d%d", &l, &r);} friend bool operator < (const node &a, const node &b){ return a.l == b.l ? a.r < b.r : a.l < b.l; } } a[N]; int T, split, tot, ans, n, m, y; int x[N], root[N], s[N], f[M], ls[M], rs[M]; int ins(int x, int l, int r, int val){ int u = ++tot; f[u] = f[x] + 1; ls[u] = ls[x]; rs[u] = rs[x]; if (l == r) return u; int mid = (l + r) >> 1; if (val <= mid) ls[u] = ins(ls[x], l, mid, val); else rs[u] = ins(rs[x], mid + 1, r, val); return u; } int query(int x, int L, int R, int l, int r){ if (l <= L && R <= r) return f[x]; int ret = 0; int mid = (L + R) >> 1; if (l <= mid) ret += query(ls[x], L, mid, l, r); if (r > mid) ret += query(rs[x], mid + 1, R, l, r); return ret; } int solve(int l1, int r1, int l2, int r2){ return query(root[r1], 1, n, l2, r2) - query(root[l1], 1, n, l2, r2); } int main(){ scanf("%d", &T); while (T--){ tot = 0; scanf("%d", &n); rep(i, 1, n) a[i].scan(); sort(a + 1, a + n + 1); for (int i = 1, j = 1; i <= n; ++i){ root[i] = root[i - 1]; for (; j <= n && a[j].l <= i; ++j) root[i] = ins(root[i], 1, n, a[j].r); } scanf("%d", &m); split = int(sqrt(n / log(n)) * 0.8); rep(op, 1, m){ ans = 0; scanf("%d", &y); rep(i, 1, y) scanf("%d", x + i); sort(x + 1, x + y + 1); x[0] = 0, x[y + 1] = n + 1; if (y > split){ s[0] = 0; for (int i = 1, j = 1; i <= n; ++i){ s[i] = s[i - 1]; for (; j <= n && x[j] <= i; ++j) ++s[i]; } rep(i, 1, n) ans += ((s[a[i].r] & 1) ^ (s[a[i].l - 1] & 1)); } else{ rep(i, 1, y){ for (int j = i; j <= y; j += 2){ ans += solve(x[i - 1], x[i], x[j], x[j + 1] - 1); } } } printf("%d\n", ans); } } return 0; }