[BZOJ4605]崂山白花蛇草水(主席树套kd-tree)

题意:两种操作,在二维平面插入一个点及其权值,查询矩形区间第k大,强制在线。

之前考试考过一个矩形区间第k大的题。。当时想了各种树套树套树,算了算复杂度都没有暴力快。。后来憋了个kd-tree套主席树,就是把若干个kd-tree上的节点上的主席树弄来一起走,时间复杂度logn*sqrt(n),空间复杂度俩log(由于那个题是离线的,我当时写的kd-tree上自底向上的线段树合并,所以实际空间复杂度只有一个log)。。当时标程给的块状链表套划分树(维度高了什么奇葩玩意都有),比我慢一倍。。然后前两天BZOJ上加了俩差不多的题,就把当时的代码改了改交了一发,结果被claris狂D了一顿。。于是决心痛改前非把主席树放外面。。

主席树放外面的好处就是空间上少个log,并且不用将一大堆主席树节点存在数组里一起跑。。维度比较高的题目当中最好把平衡树或者kd-tree(本质和平衡树差不多)放里面,这样内部插入一个节点的内存开销是O(1)的。感觉主席树放外面本质其实就是动态版的划分树,因为划分树里面是数组,不支持一些动态操作,所以根据具体题目改成平衡树啊kd-tree啊什么什么的就好了。。

开始偷懒没写重构T了一发。。

加上了替罪羊还是比claris慢三倍TAT。。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define erp(i,a,b) for(int i=a;i>=b;--i)
using namespace std;
inline void gmin(int&a, const int&b) { if(a>b)a=b; }
inline void gmax(int&a, const int&b) { if(a<b)a=b; }
const int MAXN = 100005;
const int inf = 1000000000;
const int MAXS = 2000000;
const double alp = 0.8;
int N, Q;

struct dot {
	int d[2], mn[2], mx[2], l, r, sz;
	dot () { l = r = 0; }
	dot (int x, int y) { d[0] = x; d[1] = y; l = r = 0; }
	int& operator [] (int x) { return d[x]; }
};
int D, pt[MAXN];
int dcnt, tot;
namespace kdt
{
	dot tr[MAXS];
	inline bool cmp(int i, int j)
	{
		return tr[i][D] < tr[j][D];
	}
	inline void pushup(int k)
	{
		dot&l = tr[tr[k].l], &r = tr[tr[k].r];
		tr[k].mn[0] = tr[k].mx[0] = tr[k][0];
		tr[k].mn[1] = tr[k].mx[1] = tr[k][1];
		rep(i, 0, 1)
		{
			if (tr[k].l) gmin(tr[k].mn[i], l.mn[i]), gmax(tr[k].mx[i], l.mx[i]);
			if (tr[k].r) gmin(tr[k].mn[i], r.mn[i]), gmax(tr[k].mx[i], r.mx[i]);
		}
		tr[k].sz = l.sz + r.sz + 1;
	}
	inline int NewDot(int x, int y)
	{
		++dcnt, tr[dcnt][0] = x, tr[dcnt][1] = y;
		return pushup(dcnt), dcnt;
	}
	inline bool isbad(int x)
	{
		return max(tr[tr[x].l].sz, tr[tr[x].r].sz) > tr[x].sz*alp+5;
	}
	int quary(int i, int x0, int y0, int x1, int y1)
	{
		if (!i||tr[i].mn[0]>x1||tr[i].mx[0]<x0||tr[i].mn[1]>y1||tr[i].mx[1]<y0) return 0;
		if (tr[i].mn[0]>=x0&&tr[i].mx[0]<=x1&&tr[i].mn[1]>=y0&&tr[i].mx[1]<=y1) return tr[i].sz;
		int ret = 0;
		if (tr[i][0]>=x0&&tr[i][0]<=x1&&tr[i][1]>=y0&&tr[i][1]<=y1) ret ++;
		return ret + quary(tr[i].l, x0, y0, x1, y1) + quary(tr[i].r, x0, y0, x1, y1);
	}
	int gt, gtd, gtf;
	void ins(int&x, int D, const dot&p)
	{
		if (!x) { x = NewDot(p.d[0], p.d[1]); return; }
		if (p.d[D]<tr[x][D]) ins(tr[x].l, D^1, p);
		else ins(tr[x].r, D^1, p);
		pushup(x);
		if (isbad(x)) gt = x, gtd = D, gtf = 0;
		else if (gt==tr[x].l||gt==tr[x].r) gtf = x;
	}
	void treavel(int&x)
	{
		if (!x) return;
		pt[++tot] = x;
		treavel(tr[x].l), treavel(tr[x].r);
	}
	int build(int l, int r, int now)
	{
		if (l > r) return 0;
		int mid = (l+r)>>1, x;
		D = now;
		nth_element(pt+l, pt+mid, pt+r+1, cmp);
		x = pt[mid];
		tr[x].l = build(l, mid-1, now^1);
		tr[x].r = build(mid+1, r, now^1);
		return pushup(x), x;
	}
	void deal_ins(int&x, const dot&p)
	{
		gt = gtf = 0, ins(x, 0, p);
		if (!gt) return;
		tot = 0, treavel(gt);
		if (!gtf) { x = build(1, tot, gtd); return; }
		if (gt==tr[gtf].l) tr[gtf].l = build(1, tot, gtd);
		else tr[gtf].r = build(1, tot, gtd);
	}
}

#define lch(a) ti[a].lch
#define rch(a) ti[a].rch
struct Node {
	int lch, rch, rt;
} ti[MAXS];
int ncnt, root;
void ins(int&x, const dot&p, int val, int L=1, int R=inf)
{
	if (!x) x = ++ncnt;
	kdt::deal_ins(ti[x].rt, p);
	if (L==R) return;
	int mid = (L+R)>>1;
	if (val<=mid) ins(lch(x), p, val, L, mid);
	else ins(rch(x), p, val, mid+1, R);
}
int quary(int&x, int&x0, int&y0, int&x1, int&y1, int k, int L=1, int R=inf)
{
	if (L==R) return L;
	int rcnt = kdt::quary(ti[rch(x)].rt, x0, y0, x1, y1);
	int mid = (L+R)>>1;
	if (k<=rcnt) return quary(rch(x), x0, y0, x1, y1, k, mid+1, R);
	return quary(lch(x), x0, y0, x1, y1, k-rcnt, L, mid);
}

int main()
{
	//freopen("data.txt","r",stdin);
	//freopen("my.out","w",stdout);
	scanf("%d%d", &N, &Q);
	int op, x0, y0, x1, y1, k, ans = 0, tmp;
	rep(i, 1, Q)
	{
		scanf("%d", &op);
		if (op==1)
		{
			scanf("%d%d%d", &x0, &y0, &k);
			x0^=ans, y0^=ans, k^=ans;
			dot p = dot(x0, y0);
			ins(root, p, k);
		}
		else
		{
			scanf("%d%d%d%d%d", &x0, &y0, &x1, &y1, &k);
			x0^=ans, y0^=ans, x1^=ans, y1^=ans, k^=ans;
			tmp = kdt::quary(ti[root].rt, x0, y0, x1, y1);
			if (tmp < k) puts("NAIVE!ORZzyz."), ans=0;
			else printf("%d\n", ans=quary(root, x0, y0, x1, y1, k));
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值