Luogu P4148 简单题(K-D Tree)

题面

题解

因为强制在线,所以我们不能$cdq$分治,所以考虑用$KDT$,$KDT$维护一个矩阵,然后询问的时候如果当前矩形在询问区间内,直接记贡献,否则判断当前点是否在矩阵内,然后左右分别递归下去判断就行了。

#include <cstdio>
#include <cstring>
#include <algorithm>
using std::min; using std::max;
using std::nth_element;
typedef long long ll;

template<typename T>
void read(T &x) {
    int flag = 1; x = 0; char ch = getchar();
    while(ch < ‘0‘ || ch > ‘9‘) { if(ch == ‘-‘) flag = -flag; ch = getchar(); }
    while(ch >= ‘0‘ && ch <= ‘9‘) x = x * 10 + ch - ‘0‘, ch = getchar(); x *= flag;
}

const int N = 200005;
int n, x1, x2, y1, y2, k, ans, rt, WD, top, cnt, rub[N];
struct poi { int x[2], w; } p[N];
struct node { int mi[2], mx[2], sum, lc, rc, siz; poi tp; } t[N];
int operator < (const poi &a, const poi &b) { return a.x[WD] < b.x[WD]; }
inline int newnode() { if(top) return rub[top--]; else return ++cnt; }
void up(int k) {
	int l = t[k].lc, r = t[k].rc;
	for(int i = 0; i < 2; ++i) {
		t[k].mi[i] = t[k].mx[i] = t[k].tp.x[i];
		if(l) t[k].mi[i] = min(t[k].mi[i], t[l].mi[i]), t[k].mx[i] = max(t[k].mx[i], t[l].mx[i]);
		if(r) t[k].mi[i] = min(t[k].mi[i], t[r].mi[i]), t[k].mx[i] = max(t[k].mx[i], t[r].mx[i]);
	}
	t[k].sum = t[l].sum + t[r].sum + t[k].tp.w, t[k].siz = t[l].siz + t[r].siz + 1;
}
int build(int l, int r, int wd) {
	if(l > r) return 0;
	int mid = (l + r) >> 1, k = newnode();
	WD = wd, nth_element(p + l, p + mid, p + r + 1), t[k].tp = p[mid];
	t[k].lc = build(l, mid - 1, wd ^ 1), t[k].rc = build(mid + 1, r, wd ^ 1);
	up(k); return k;
}
void pia(int k, int num) {
	if(t[k].lc) pia(t[k].lc, num);
	p[t[t[k].lc].siz + num + 1] = t[k].tp, rub[++top] = k;
	if(t[k].rc) pia(t[k].rc, num + t[t[k].lc].siz + 1);
}
void check(int &k, int wd) {
	if(0.75 * t[k].siz < t[t[k].lc].siz || 0.75 * t[k].siz < t[t[k].rc].siz)
		pia(k, 0), k = build(1, t[k].siz, wd);
}
void insert(int &k, poi tmp, int wd) {
	if(!k) { k = newnode(), t[k].lc = t[k].rc = 0, t[k].tp = tmp, up(k); return ; }
	if(tmp.x[wd] <= t[k].tp.x[wd]) insert(t[k].lc, tmp, wd ^ 1);
	else insert(t[k].rc, tmp, wd ^ 1);
	up(k), check(k, wd);
}
int in(int x1,int y1,int x2,int y2,int X1,int Y1,int X2,int Y2) { return (X1>=x1&&X2<=x2&&Y1>=y1&&Y2<=y2); }
int out(int x1,int y1,int x2,int y2,int X1,int Y1,int X2,int Y2) { return (x1>X2||x2<X1||y1>Y2||y2<Y1); }
int query(int k, int x1, int y1, int x2 ,int y2) {
	if(!k) return 0;
	int ret = 0;
	if(in(x1, y1, x2, y2, t[k].mi[0], t[k].mi[1], t[k].mx[0], t[k].mx[1])) return t[k].sum;
	if(out(x1, y1, x2, y2, t[k].mi[0], t[k].mi[1], t[k].mx[0], t[k].mx[1])) return 0;
	if(in(x1, y1, x2, y2, t[k].tp.x[0], t[k].tp.x[1], t[k].tp.x[0], t[k].tp.x[1])) ret += t[k].tp.w;
	return ret + query(t[k].lc, x1, y1, x2, y2) + query(t[k].rc, x1, y1, x2, y2);
}

int main () {
	read(n);
	while(true) {
		int opt; read(opt);
		if(opt == 3) break;
		else {
			read(x1), read(y1), x1 ^= ans, y1 ^= ans;
			if(opt == 1) read(k), insert(rt, (poi){x1, y1, k ^ ans}, 0);
			else {
				read(x2), read(y2), x2 ^= ans, y2 ^= ans;
				ans = query(rt, x1, y1, x2, y2), printf("%d\n", ans);
			}
		}
	}
    return 0;
}

原文地址:https://www.cnblogs.com/water-mi/p/10172376.html

时间: 2024-10-08 18:36:42

Luogu P4148 简单题(K-D Tree)的相关文章

luogu P4148 简单题

传送门 这题真简单,直接把\(CDQ\)给ban掉了 其实数据范围比较小可以直接二维树状数组,我们看数据范围,发现点的个数比N还小,可以考虑用一些奇怪的数据结构 说的就是你,\(KD tree\) \(KD tree\)就是一个K维的二叉查找树(是吧),一维的二叉查找树就是我们所熟悉的平衡树,所以考虑用平衡树的方法维护,插入一个点就跟平衡树插一个点类似.查找的话,如果当前范围完全被查询范围完全包含就直接加上范围值,如果有交就递归处理,没交就不管 注意维护整棵\(KD tree\)使其平衡,可以考

简单题(K-D Tree)

简单题不简单-- 我们把单点加操作改成插入一个权值为增加量的点,将问题转化成询问一个矩阵中所有点的和,用 \(K-D\ Tree\) 维护,时间复杂度 \(O(n\sqrt{n})\) \(Code\ Below:\) // luogu-judger-enable-o2 #include <bits/stdc++.h> using namespace std; const int maxn=500000+10; const double alpha=0.75; int n,D,rt,cnt,t

P4148 简单题(KDTree)

传送门 KDTree 修改权值当做插入节点,不平衡就暴力重构,询问的时候判断当前节点代表的矩形是否在询问的矩形的,是的话返回答案,相离返回0,否则的话判断当前点是否在矩形内,然后继续递归下去 //minamoto #include<bits/stdc++.h> #define R register #define fp(i,a,b) for(R int i=a,I=b+1;i<I;++i) #define fd(i,a,b) for(R int i=a,I=b-1;i>I;--i)

Luogu P1923 求第k小的数

Luogu P1923 求第k小的数 一看这题,静态查询区间第$k$小的数,不就是可持久化线段树(主席树)的模板题吗?!(误) 直接把主席树的板子打上来?: #include<bits/stdc++.h> #define N 200010 #define mid ((l+r)>>1) using namespace std; int n,m,l,r,k,ans,id,siz; int a[N],b[N]; struct segmenttree { int ls,rs,sum,roo

poj2105 IP Address(简单题)

题目链接:http://poj.org/problem?id=2105 Description Suppose you are reading byte streams from any device, representing IP addresses. Your task is to convert a 32 characters long sequence of '1s' and '0s' (bits) to a dotted decimal format. A dotted decima

数论 --- 简单题

吃糖果 Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)Total Submission(s): 22376    Accepted Submission(s): 6396 Problem Description HOHO, 终于从Speakless手上赢走了所有的糖果,是Gardon吃糖果时有个特殊的癖好,就是不喜欢将一样的糖果放在一起吃,喜欢先吃一种,下一次吃另一 种,这样:

BZOJ 2683 简单题 ——CDQ分治

简单题 #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define F(i,j,k) for (int i=j;i<=k;++i) #define D(i,j,k) for (int i=j;i>=k;--i) #define maxn 2000005 int sum[maxn]; void a

HNU 12868 Island (简单题)

题目链接:http://acm.hnu.cn/online/?action=problem&type=show&id=12868&courseid=272 解题报告:输入n*m的地图,+表示土地,-表示水,要你求这个海岛的海岸线有多长,扫一遍就可以了. 1 #include<cstdio> 2 const int maxn = 2000; 3 char map[maxn][maxn]; 4 int _x[4] = {-1,0,1,0}; 5 int _y[4] = {0

hdu 1201 18岁生日 (简单题)

18岁生日 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 18281    Accepted Submission(s): 5776 Problem Description Gardon的18岁生日就要到了,他当然很开心,可是他突然想到一个问题,是不是每个人从出生开始,到达18岁生日时所经过的天数都是一样的呢?似乎并不全都是这样,所以他