【bzoj5183】[Baltic2016]Park 离线+对偶图+并查集

题目描述

在Byteland的首都,有一个矩形围栏围起来的公园。在这个公园里树和访客都以一个圆形表示。公园有四个出入口,每个角落一个(1=左下角,2=右下角,3=右上角,4=左上角)。访客能通过这些出入口进出公园。访客在同时碰到一个角落的两条边时就可以通过该角落进出公园。访客在公园里可以自由地移动,但他们不能和树和围栏相交。对于每个访客,给定他们进入公园的出入口,你的任务是计算他们能在哪个出入口离开公园。

输入

输入的第一行包含两个整数:n,m:树的数量和访客的数量。

第二行包含两个整数:w,h:公园的宽和高。左下角的坐标是(0,0),右上角的坐标是(w,h)。

接下来的n行描述每棵树。

每行包含3个整数:x,y,r:树的圆心是(x,y),半径是r。每棵树都不会和其他树和围栏相交。

最后m行描述每个访客。每行包含2个整数:r,e:访客的半径和他们进入公园的出入口。

数据保证没有一棵树会和每个角落的2k*2k的方形区域相交,k是最大的访客半径。

1<=N<=2000

1<=M<=100000

4k<w,h<=10^9

输出

对于每个访客你要输出一行,包含了他们能在哪些出入口离开公园,以从小到大的顺序输出,不需要空格分隔。

样例输入

5 3
16 11
11 8 1
6 10 1
7 3 2
10 4 1
15 5 1
1 1
2 2
2 1

样例输出

1234
2
14



题解

离线+对偶图+并查集

套路:如果两棵树或树与边界之间无法通过,则视作它们之间连了一条边。

那么左下角能到右下角等价于:下边界与左、上、右边界都不连通。其余同理。

然而每个人的半径都不同,对每个人单独处理必T无疑。考虑离线,将人按照半径、树与树和树与边界按照能通过的最大距离从小到大排序然后处理即可。

连通性可以直接使用并查集维护。

时间复杂度 $O(n^2\log n+m\log m+m\times 常数)$

#include <cmath>
#include <cstdio>
#include <algorithm>
#define squ(x) (x) * (x)
using namespace std;
struct data
{
	double d;
	int x , y;
	data() {}
	data(double a , int b , int c) {d = a , x = b , y = c;}
	bool operator<(const data &a)const {return d < a.d;}
}a[2007010] , q[100010];
double px[2010] , py[2010] , pr[2010];
int f[2010] , ans[100010] , tot;
int find(int x)
{
	return x == f[x] ? x : f[x] = find(f[x]);
}
int main()
{
	int n , m , i , j , p = 1 , t1 , t2 , t3 , t4;
	double w , h;
	scanf("%d%d%lf%lf" , &n , &m , &w , &h);
	for(i = 1 ; i <= n ; i ++ )
	{
		scanf("%lf%lf%lf" , &px[i] , &py[i] , &pr[i]);
		a[++tot] = data(px[i] - pr[i] , i , n + 1);
		a[++tot] = data(py[i] - pr[i] , i , n + 2);
		a[++tot] = data(w - px[i] - pr[i] , i , n + 3);
		a[++tot] = data(h - py[i] - pr[i] , i , n + 4);
		for(j = 1 ; j < i ; j ++ ) a[++tot] = data(sqrt(squ(px[i] - px[j]) + squ(py[i] - py[j])) - pr[i] - pr[j] , i , j);
	}
	for(i = 1 ; i <= m ; i ++ ) scanf("%lf%d" , &q[i].d , &q[i].x) , q[i].d *= 2 , q[i].y = i;
	sort(a + 1 , a + tot + 1) , sort(q + 1 , q + m + 1);
	for(i = 1 ; i <= n + 4 ; i ++ ) f[i] = i;
	for(i = 1 ; i <= m ; i ++ )
	{
		while(p <= tot && a[p].d < q[i].d) f[find(a[p].x)] = find(a[p].y) , p ++ ;
		t1 = find(n + 1);
		t2 = find(n + 2);
		t3 = find(n + 3);
		t4 = find(n + 4);
		if(q[i].x == 1)
		{
			ans[q[i].y] |= (1 << 1);
			if(t2 != t1 && t2 != t3 && t2 != t4) ans[q[i].y] |= (1 << 2);
			if(t1 != t2 && t1 != t3 && t2 != t4 && t3 != t4) ans[q[i].y] |= (1 << 3);
			if(t1 != t2 && t1 != t3 && t1 != t4) ans[q[i].y] |= (1 << 4);
		}
		else if(q[i].x == 2)
		{
			ans[q[i].y] |= (1 << 2);
			if(t2 != t1 && t2 != t3 && t2 != t4) ans[q[i].y] |= (1 << 1);
			if(t3 != t1 && t3 != t2 && t3 != t4) ans[q[i].y] |= (1 << 3);
			if(t1 != t3 && t1 != t4 && t2 != t3 && t2 != t4) ans[q[i].y] |= (1 << 4);
		}
		else if(q[i].x == 3)
		{
			ans[q[i].y] |= (1 << 3);
			if(t1 != t2 && t1 != t3 && t2 != t4 && t3 != t4) ans[q[i].y] |= (1 << 1);
			if(t3 != t1 && t3 != t2 && t3 != t4) ans[q[i].y] |= (1 << 2);
			if(t4 != t1 && t4 != t2 && t4 != t3) ans[q[i].y] |= (1 << 4);
		}
		else
		{
			ans[q[i].y] |= (1 << 4);
			if(t1 != t2 && t1 != t3 && t1 != t4) ans[q[i].y] |= (1 << 1);
			if(t1 != t3 && t1 != t4 && t2 != t3 && t2 != t4) ans[q[i].y] |= (1 << 2);
			if(t4 != t1 && t4 != t2 && t4 != t3) ans[q[i].y] |= (1 << 3);
		}
	}
	for(i = 1 ; i <= m ; i ++ )
	{
		for(j = 1 ; j <= 4 ; j ++ )
			if(ans[i] & (1 << j))
				printf("%d" , j);
		puts("");
	}
	return 0;
}

原文地址:https://www.cnblogs.com/GXZlegend/p/8485421.html

时间: 2024-10-09 01:51:16

【bzoj5183】[Baltic2016]Park 离线+对偶图+并查集的相关文章

【bzoj4423】[AMPPZ2013]Bytehattan 对偶图+并查集

题目描述 比特哈顿镇有n*n个格点,形成了一个网格图.一开始整张图是完整的.有k次操作,每次会删掉图中的一条边(u,v),你需要回答在删除这条边之后u和v是否仍然连通. 输入 第一行包含两个正整数n,k(2<=n<=1500,1<=k<=2n(n-1)),表示网格图的大小以及操作的个数.接下来k行,每行包含两条信息,每条信息包含两个正整数a,b(1<=a,b<=n)以及一个字符c(c=N或者E).如果c=N,表示删除(a,b)到(a,b+1)这条边:如果c=E,表示删除

【bzoj3007】拯救小云公主 二分+对偶图+并查集

题目描述 英雄又即将踏上拯救公主的道路…… 这次的拯救目标是——爱和正义的小云公主. 英雄来到boss的洞穴门口,他一下子就懵了,因为面前不只是一只boss,而是上千只boss.当英雄意识到自己还是等级1的时候,他明白这就是一个不可能完成的任务. 但他不死心,他在想,能不能避开boss去拯救公主呢,嘻嘻. Boss的洞穴可以看成一个矩形,英雄在左下角(1,1),公主在右上角(row,line).英雄为了避开boss,当然是离boss距离越远越好了,所以英雄决定找一条路径使到距离boss的最短距离

URAL1671 Anansi&#39;s Cobweb(离线做 + 并查集)

传送门 大意:给出一个无向图,删除Q条边,每删除一次就询问一次目前的连通块的数目. 思路:离线搞, 把删边转换为加边,每加一次边,若两个顶点不连通就用并查集把着这两个连通块合并. 代码: #include<cstdio> #include<cstring> #include<algorithm> #define MAXN 100005 using namespace std; int n, m, q; int s[MAXN], t[MAXN]; int ban[MAXN

【bzoj4551】[Tjoi2016&amp;Heoi2016]树 离线处理+并查集

题目描述 在2016年,佳媛姐姐刚刚学习了树,非常开心.现在他想解决这样一个问题:给定一颗有根树(根为1),有以下两种操作:1. 标记操作:对某个结点打上标记(在最开始,只有结点1有标记,其他结点均无标记,而且对于某个结点,可以打多次标记.)2. 询问操作:询问某个结点最近的一个打了标记的祖先(这个结点本身也算自己的祖先)你能帮帮他吗? 输入 输入第一行两个正整数N和Q分别表示节点个数和操作次数接下来N-1行,每行两个正整数u,v(1≤u,v≤n)表示u到v有一条有向边接下来Q行,形如"oper

HDU 5441 离线处理 + 并查集

题意:给n个节点m条带权值边的无向图.然后q个问题,每次询问点对的数目,点对需要满足的条件是:1)连通:2)其路径的最大权值不能超过询问值. 分析:如果没次询问一次,dfs一次,很可能超时,因此可以用并查集.离线处理,把边按权值排序,把问题按大小排序.然后离线的过程就是不断向图中加边的过程. 比如样例如下: 然后离线处理,排完序后将会是一条一条的加边:问题也排了序,因此是个累加过程... 1 #include <cstdio> 2 #include <iostream> 3 #in

BZOJ-4423 : [AMPPZ2013]Bytehattan (对偶图+并查集)

4423: [AMPPZ2013]Bytehattan Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 189  Solved: 122[Submit][Status][Discuss] Description 比特哈顿镇有n*n个格点,形成了一个网格图.一开始整张图是完整的.有k次操作,每次会删掉图中的一条边(u,v),你需要回答在删除这条边之后u和v是否仍然连通. Input 第一行包含两个正整数n,k(2<=n<=1500,1<=k&l

【FZU】Problem 2059 MM(离线处理并查集)

离线处理,并查集 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn = 100005; struct Node{ int id,value; }node[maxn],input[maxn]; bool cmp(Node p,Node q){ return p.value > q.value; } int p[maxn],vis[max

hdu3938 Portal 离线的并查集

离线算法是将全部输入都读入,计算出所有的答案以后再输出的方法.主要是为避免重复计算.类似于计算斐波那契数列的时候用打表的方法. 题目:给一个无向图,求有多少个点对,使得两点间的路径上的花费小于L,这里路径上的花费是这样规定的,a.b两点之间所有的路径中的最大边的最小值.    当然题目上不是这么写的.它问的是有多少种路径,这里就比较模糊了,到底两个路径怎样才算是两种路径呢,这时候重新看题,可以发现,如果理解为路径中经过的点不同的话,题目中给的所谓两点间的花费这个定义就没有意义了,所以就可以猜测,

[hdu4585]离线,并查集

题意:把一些数加到集合里面,每个数有两个属性,分别是编号和值,加进去之前询问集合里面距离自己“最近”的数的编号.最近的意思是值的差的绝对值最小,如果有相等的,则取值小的.并且已知所有数的id和value都是唯一的. 思路:把处理过程反过来,就变成了一次把一个点删除,删除可以用数组标记,而询问则转化为找某个数左边的第一个没标记的数和右边的第一个没被标记的数,由于删除是永久的,所以完全可以通过并查集来加速,标记也可以省略. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16