1. 程式人生 > >CodeChef February Challenge 2018 Chef and odd queries (分塊 + 主席樹)

CodeChef February Challenge 2018 Chef and odd queries (分塊 + 主席樹)

turn () += -- oid sca com print const

題目鏈接 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;
}

  

CodeChef February Challenge 2018 Chef and odd queries (分塊 + 主席樹)