zoukankan      html  css  js  c++  java
  • 【ybt金牌导航4-2-1】【luogu P4169】[Violet]天使玩偶 / SJY摆棋子(K-D tree 模板)

    [Violet]天使玩偶 / SJY摆棋子

    题目链接:ybt金牌导航4-2-1 / luogu P4169

    题目大意

    初始有一些点,分布在二维平面上。
    然后要你进行一些操作:再往上面放一个点,或者给你一个坐标,找到距离它最近的点到它的距离。
    (这里的距离是哈密顿距离,就 x,y 坐标差的和)

    思路

    这道题我们用 K-D tree 来做。
    (它其实可以说是 K-D tree 的模板题?)

    那就来略讲一下 K-D tree。
    K-D tree 就是一个可以存 (k) 维的点一个二叉树。
    然后你这个 K-D tree 就相当于对这些点构成的 (k) 维空间的一个划分,树中每个点代表一块空间。
    而这道题中我们用的是二维 K-D tree,一般用到的也是这个。

    它大概是这样,每个深度有一个划分的维度 (d),然后在这个深度的点中,它的左儿子的 (d) 维信息小于它的,它右儿子的 (d) 维信息大于它的。
    那很容易看出你划分的维度当然是轮流来的,就比如先第一维,接着第二维,然后第三维,再变成第一维,以此类推。
    (不过说一开始轮的顺序也可以按方差从大到小排,可以缩小时间)

    然后划分的依据,容易想到我们要让树尽可能平衡,那我们构树的时候就选中位数作为代表的点,然后再把其他点分到左右两边。(可以用 nth-element)
    然后容易看出它是可以不断新放别的点的,就按照每个点,从根节点一直往下走,走到没有点的地方就放在那里。

    但是你会想到放多了它可能就不平衡了,那就用替罪羊的思想,也搞个失衡的值。
    如果两个子树有一个的个数超过它的个数乘上这个值,就把这个树拍扁重构。

    那搞到这一题大概就是你每个点代表一个矩形,而且每个点在树上都有一个代表的点。
    然后对于这个节点代表的点,我们求出它到询问给出点的距离。
    然后再搞出它两个儿子代表的矩阵到询问点的最短距离。
    那这个距离就是这个矩阵里面的点到这个询问点距离的下界,那如果这个下界都比你当前的答案大,那就没有必要跑这个矩阵里面的点了。
    (算点到矩阵距离大概就是看四条边,点到四条边的距离)

    然后还有一个优化就是,我们可以先跑下界小的矩阵,再跑下界大的。
    因为这样答案会更新的更小一点,就可以跳过更多的矩阵。

    然后大概这样就好了。
    (主要是实现烦)

    代码

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #define INF 1e9
    #define alph (0.7)//这个是判断不平衡的(类似替罪羊)
    #define rr register
    
    using namespace std;
    
    struct zb {
    	int w[2];
    }a[1000001], tmp;
    struct node {
    	zb a;
    	int l, r, ma[2], mi[2], size;
    }tree[1000001];
    int n, m, op, root, tot, ans;
    int rebuild[1000001], WD;
    
    int read() {
    	int re = 0;
    	char c = getchar();
    	while (c < '0' || c > '9') c = getchar();
    	while (c >= '0' && c <= '9') {
    		re = (re << 3) + (re << 1) + c - '0';
    		c = getchar();
    	}
    	return re;
    }
    
    void write(rr int now) {
    	if (now > 9) write(now / 10);
    	putchar(now % 10 + '0');
    }
    
    int operator <(zb x, zb y) {//这个是用来作为 nth-element 函数比较的依据的
    	return x.w[WD] < y.w[WD];
    }
    
    int get_point() {//开一个新的点
    	if (rebuild[0]) return rebuild[rebuild[0]--];//如果是重构就把重构的点拿出来
    	return ++tot;
    }
    
    int abss(rr int now) {
    	if (now < 0) return -now;
    	return now;
    }
    
    int up(rr int now) {//上传值:坐标最大,最小(其实就是哪个矩阵的位置),里面点的个数
    	for (int i = 0; i < 2; i++) {
    		tree[now].mi[i] = tree[now].a.w[i];
    		tree[now].ma[i] = tree[now].a.w[i];
    		if (tree[now].l) {
    			tree[now].mi[i] = min(tree[now].mi[i], tree[tree[now].l].mi[i]);
    			tree[now].ma[i] = max(tree[now].ma[i], tree[tree[now].l].ma[i]);
    		}
    		if (tree[now].r) {
    			tree[now].mi[i] = min(tree[now].mi[i], tree[tree[now].r].mi[i]);
    			tree[now].ma[i] = max(tree[now].ma[i], tree[tree[now].r].ma[i]);
    		}
    	}
    	tree[now].size = tree[tree[now].l].size + tree[tree[now].r].size + 1;
    }
    
    int build(rr int l, rr int r, rr int wd) {
    	if (l > r) return 0;
    	
    	rr int now = get_point();
    	rr int mid = (l + r) >> 1;
    	WD = wd;
    	nth_element(a + l, a + mid, a + r + 1);
    	tree[now].a = a[mid];//把中间那个搞出来
    	
    	tree[now].l = build(l, mid - 1, wd ^ 1);
    	tree[now].r = build(mid + 1, r, wd ^ 1);
    	
    	up(now);
    	return now;
    }
    
    void make_again(rr int root, rr int num) {//拍扁
    	if (tree[root].l) make_again(tree[root].l, num);
    	a[num + tree[tree[root].l].size + 1] = tree[root].a;
    	rebuild[++rebuild[0]] = root;
    	if (tree[root].r) make_again(tree[root].r, num + tree[tree[root].l].size + 1);
    }
    
    void check(rr int &root, rr int wd) {
    	if (alph * tree[root].size < tree[tree[root].l].size || alph * tree[root].size < tree[tree[root].r].size) {//不平衡了
    		make_again(root, 0);//拍扁
    		root = build(1, tree[root].size, wd);//重构
    	}
    }
    
    void insert(rr zb now, rr int &root, rr int wd) {//插入点
    	if (!root) {//已经找到位置了
    		root = get_point();
    		tree[root].a = now;
    		tree[root].l = tree[root].r = 0;
    		up(root);
    		return ;
    	}
    	
    	if (tree[root].a.w[wd] < now.w[wd])//判断它应该放到哪边
    		insert(now, tree[root].r, wd ^ 1);
    	else insert(now, tree[root].l, wd ^ 1);
    	
    	up(root);
    	check(root, wd);
    }
    
    int get_dis(rr zb x, rr zb y) {//求哈密顿距离
    	return abss(x.w[0] - y.w[0]) + abss(x.w[1] - y.w[1]); 
    }
    
    int blog_dis(rr zb x, rr int now) {//求一个点到一个矩阵的哈密顿距离
    	rr int re = 0;
    	for (int i = 0; i < 2; i++) {
    		re += max(0, tree[now].mi[i] - x.w[i]);//分别从这个矩阵这个维度的两个(边)
    		re += max(0, x.w[i] - tree[now].ma[i]);
    	}
    	return re;
    }
    
    void get_close(rr zb now, rr int root) {//找到距离这个点最近的点
    	ans = min(ans, get_dis(now, tree[root].a));//先跟中位数代表的点算距离
    	rr int ldis = INF, rdis = INF;
    	if (tree[root].l) ldis = blog_dis(now, tree[root].l);
    	if (tree[root].r) rdis = blog_dis(now, tree[root].r);
    	
    	if (ldis < rdis) {//先搜答案下界小的(搜之前还要判断是否可能比答案小)
    		if (ldis < ans) get_close(now, tree[root].l);
    		if (rdis < ans) get_close(now, tree[root].r);
    	}
    	else {
    		if (rdis < ans) get_close(now, tree[root].r);
    		if (ldis < ans) get_close(now, tree[root].l);
    	}
    }
    
    int main() {
    //	freopen("read.txt", "r", stdin);
    //	freopen("write.txt", "w", stdout);
    	
    	n = read();
    	m = read();
    	for (rr int i = 1; i <= n; i++) {
    		a[i].w[0] = read();
    		a[i].w[1] = read();
    	}
    	
    	root = build(1, n, 0);
    	
    	while (m--) {
    		op = read();
    		tmp.w[0] = read();
    		tmp.w[1] = read();
    		if (op == 1) {
    			insert(tmp, root, 0);
    		}
    		else {
    			ans = INF;
    			get_close(tmp, root);
    			write(ans);
    			putchar('
    ');
    		}
    	}
    	
    	return 0;
    }
    
  • 相关阅读:
    R 画structure图
    perl ExtUtils::Manifest
    解决libstdc++.so.5问题
    R ggplot2 线性回归
    perl 计算方差中值平均数 Statistics::Descriptive;
    专题(十一)scp
    tcpdump的使用
    专题 (四) sed
    python 学习笔记(二)之 字符串操作
    redis 学习笔记之(二)主备高可用(热备)
  • 原文地址:https://www.cnblogs.com/Sakura-TJH/p/YBT_JPDH_4-2-1.html
Copyright © 2011-2022 走看看