JZOJ 5067. 【GDOI2017第二轮模拟day2】有理有据题 (KD-tree+历史最值问题)

https://gmoj.net/senior/#main/show/5067

题解:

考虑\([l,r]∩[x,y] \ne ∅\)的充要条件是\(max(l,x)\le min(r,y)\)
即\(l \le y且 x \le r\)

那么每次相当于修改矩形内的点。

套上一个K-D tree,问题变成了:

  1. 区间加
  2. 区间赋值
  3. 询问单点历史最大值

这个可以直接lazytag搞定,通过思考一个操作序列需要保留什么东西即可实现。

Code:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;

const int N = 50005;

int n, m, Q;

struct P {
	int x, y, i;
} a[N];

void Init() {
	scanf("%d %d %d", &n, &m, &Q);
	fo(i, 1, n) {
		scanf("%d %d", &a[i].x, &a[i].y);
		if(a[i].x > a[i].y) swap(a[i].x, a[i].y);
		a[i].y = -a[i].y;
		swap(a[i].x, a[i].y);
		a[i].i = i;
	}
}

struct nod {
	bool bz;
	int m1, m2, s;
	nod() { bz = m1 = m2 = s = 0;}
};

void jia(nod &a, int v) {
	if(!a.bz) {
		a.s += v;
		a.m1 = max(a.m1, a.s);
	} else {
		a.s += v;
		a.m2 = max(a.m2, a.s);
	}
}

void fz(nod &a) {
	a.bz = 1;
	a.s = 0;
}

void mer(nod &a, nod b) {
	if(!a.bz) {
		if(!b.bz) {
			a.bz = 0;
			a.m1 = max(a.m1, a.s + b.m1);
			a.s += b.s;
		} else {
			a.bz = 1;
			a.m1 = max(a.m1, a.s + b.m1); 
			a.m2 = b.m2;
			a.s = b.s;
		}
	} else {
		if(!b.bz) {
			a.bz = 1;
			a.m2 = max(a.m2, a.s + b.m1);
			a.s += b.s;
		} else {
			a.bz = 1;
			a.m2 = max(a.m2, max(a.s + b.m1, b.m2));
			a.s = b.s;
		}
	}
}

#define i0 i + i
#define i1 i + i + 1

struct tree {
	int mi[2], mx[2];
	nod v;
} t[N * 4];
int rt = 1;

int o;
int cmp(P a, P b) {
	return o ? a.y < b.y : a.x < b.x;
}

int ia[N];

#define db double
int ca(int x, int y) {
	db s1 = 0, s2 = 0;
	fo(i, x, y) {
		s1 += a[i].x;
		s2 += a[i].y;
	}
	s1 /= (y - x + 1);
	s2 /= (y - x + 1);
	db v1 = 0, v2 = 0;
	#define sqr(x) ((x) * (x))
	fo(i, x, y) {
		v1 += sqr(a[i].x - s1);
		v2 += sqr(a[i].y - s2);
	}
	return v1 > v2 ? 0 : 1;
}

void bt(int i, int x, int y) {
	if(x == y) {
		t[i].mi[0] = t[i].mx[0] = a[x].x;
		t[i].mi[1] = t[i].mx[1] = a[x].y;
		ia[a[x].i] = x;
		return;
	}
	o = ca(x, y)	;
	int m = x + y >> 1;
	nth_element(a + x, a + m, a + y + 1, cmp);
	
	bt(i0, x, m); bt(i1, m + 1, y);
	
	fo(j, 0, 1) {
		t[i].mi[j] = min(t[i0].mi[j], t[i1].mi[j]);
		t[i].mx[j] = max(t[i0].mx[j], t[i1].mx[j]);
	}
}

void down(int i) {
	if(t[i].v.bz || t[i].v.m1 || t[i].v.s) {
		mer(t[i0].v, t[i].v);
		mer(t[i1].v, t[i].v);
		t[i].v = nod();
	}
}

int pl[2], pr[2];

void add(int i, int x, int y) {
	if(t[i].mi[0] > pr[0] || t[i].mi[1] > pr[1]) {
		fz(t[i].v);
		return;
	}
	if(t[i].mx[0] <= pr[0] && t[i].mx[1] <= pr[1]) {
		jia(t[i].v, 1);
		return;
	}
	int m = x + y >> 1; down(i);
	add(i0, x, m); add(i1, m + 1, y);
}

int pz, px;

void ft(int i, int x, int y) {
	if(x == y) {
		px = max(t[i].v.m1, t[i].v.m2);
		return;
	}
	int m = x + y >> 1; down(i);
	if(pz <= m) ft(i0, x, m); else ft(i1, m + 1, y);
}

char str[11];
int x, y;

void cz(int x, int y) {
	if(x > y) swap(x, y);
	x = -x;
	pl[0] = pl[1] = -1e9;
	pr[0] = x, pr[1] = y;
	add(rt, 1, n);
}

int qry(int x) {
	pz = ia[x]; px = 0;
	ft(rt, 1, n);
}

void End() {
	fo(i, 1, m) {
		scanf("%d %d", &x, &y);
		cz(x, y);
	}
	fo(i, 1, Q) {
		scanf("%s", str + 1);
		if(str[1] == 'A') {
			scanf("%d %d", &x, &y);
			cz(x, y);
		}
		if(str[1] == 'C') {
			scanf("%d", &x);
			pp("%d\n", qry(x));
		}
		if(str[1] == 'Q') {
			int s = 0;
			fo(j, 1, n) s ^= qry(j);
			pp("%d\n", s);
		}
	}
}

int main() {
	freopen("bomb.in", "r", stdin);
	freopen("bomb.out", "w", stdout);
	Init();
	bt(rt, 1, n);
	End();
}
上一篇:P3951【NOIP2017 提高组】小凯的疑惑 题解


下一篇:JZOJ 6678. 【2020.05.01省选模拟】苏菲的世界 (simpson积分+几何法求多个圆的并的面积)