Description
An earthquake takes place in Southeast Asia. The ACM (Asia Cooperated Medical team) have set up a wireless network with the lap computers, but an unexpected aftershock attacked, all computers in the network were all broken. The computers are repaired one by one, and the network gradually began to work again. Because of the hardware restricts, each computer can only directly communicate with the computers that are not farther than d meters from it. But every computer can be regarded as the intermediary of the communication between two other computers, that is to say computer A and computer B can communicate if computer A and computer B can communicate directly or there is a computer C that can communicate with both A and B.
In the process of repairing the network, workers can take two kinds of operations at every moment, repairing a computer, or testing if two computers can communicate. Your job is to answer all the testing operations.
Input
The first line contains two integers N and d (1 <= N <= 1001, 0 <= d <= 20000). Here N is the number of computers, which are numbered from 1 to N, and D is the maximum distance two computers can communicate directly. In the next N lines, each contains two integers xi, yi (0 <= xi, yi <= 10000), which is the coordinate of N computers. From the (N+1)-th line to the end of input, there are operations, which are carried out one by one. Each line contains an operation in one of following two formats:
- “O p” (1 <= p <= N), which means repairing computer p.
- “S p q” (1 <= p, q <= N), which means testing whether computer p and q can communicate.
The input will not exceed 300000 lines.
Output
For each Testing operation, print “SUCCESS” if the two computers can communicate, or “FAIL” if not.
Sample Input
4 1
0 1
0 2
0 3
0 4
O 1
O 2
O 4
S 1 4
O 3
S 1 4
Sample Output
FAIL
SUCCESS
题意:
题目大意是说有一些电脑,编号为1到N,现在这些电脑坏了,无法相互连通,我们需要维修,输入首先输入N和d,N表示有多少台电脑,d表示两台已维修好的电脑若它们之间的距离小于等于d,则两台电脑可以互通。接下来输入N行,每行输入a,b两个数,N行中的第i行表示编号为i的电脑的坐标(用来求两台电脑的距离),在接下来的输入各种操作,O a表示编号为a的电脑被维修好了,S a b则表示询问编号为a和b的电脑能不能互通,若能则输出SUCCESS,若不能则输出FAIL。
思路:
首先先来分析样例,一开始所有电脑都是坏的,然后显示O 1操作,代表了编号为1的电脑被修好,发现之前没有修好的电脑,也就表示编号为1的电脑(后面简称1号)没有可以互通的电脑,接着是2号被修好,发现2号与1号的距离为1,所以2号和1号可以互通,所以对2号和1号执行连接(link)操作,接着是4号被修好,发现1号和2号与4号之间的距离都大于d,也就是说没有连接操作,这是有一个S 1 4询问,可以发现1号和4号并不能互通,因此输出FAIL,接下来修好3号,并且进行2号和3号、4号和3号的连接操作,最后的询问发现1号和4号可以通过2号和3号连接,所以输出SUCCESS。
由上面的分析可以清晰地发现这就是个并查集的题,接下来我们来模拟下样例,首先我们定义一个par数组,用来存放每个结点的父亲节点,以自己为父亲的节点就是最终节点,所以par数组的初始状态为:
下标 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
par | 1 | 2 | 3 | 4 | 5 |
接下来的连接操作是1号和2号,所以par数组修改为:
下标 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
par | 1 | 1 | 3 | 4 | 5 |
再接着的连接操作是2号和3号,首先我们发现2号节点的par是1,说明2号不是最终节点,所以我们要找到1号,发现1号是最终节点,而3号也是最终节点,所以将3号节点的父节点置为1,如下:
下标 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
par | 1 | 1 | 1 | 4 | 5 |
看着这个数组,我们只需要判断两个节点的最终节点是否相等,就可以知道两个节点是否互通,例如此时判断1和4号是否互通,此时1号的最终节点是1号,4号的最终节点是4号,所以不想等,所以不互通。
最后被修复的是4号,所以par数组修改如下:
下标 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
par | 1 | 1 | 1 | 1 | 5 |
此时可见1号和4号是互通的。
这里我们需要考虑个问题,从上表可以看出上面的节点都是接在1号上面的,这样保证了树的均匀(不知道专业术语怎么说,差不多就是均匀的意思吧)。但是在代码里我们应该如何保证这个均匀呢,对此我们可以设立 一个rank数组,在连接的时候我们可以比较两个节点的rank,我们可以把rank大的节点作为rank小的节点的父亲,若两个rank相等,则成为父亲的那个节点的rank加1。
代码:
找最终节点的代码
int find(int i){
while (par[i] != i){
i = par[i];
}
return i;
}
连接结点的代码
void Link(int a, int b){
a = find(a);
b = find(b);
if (r[a] < r[b]){
par[a] = b;
}
else{
par[b] = a;
if (r[a] == r[b])
r[a]++;
}
}
总体代码
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
#include<stack>
#include<math.h>
using namespace std;
#define clr_all(a,b) memset(a,b,sizeof(a))
#define clr(a,b,n) memset(a,b,sizeof(a[0])*n)
template<typename T> T Max(T a, T b){ return a > b ? a : b; }
const int MAXN = 1001 + 10;
struct node{
int x, y;
}num[MAXN];
int par[MAXN];
int r[MAXN];
int ok[MAXN];
int tot;
void Init(){
for (int i = 0; i < MAXN; i++)
par[i] = i;
tot = 0;
clr_all(r, 0);
}
int find(int i){
while (par[i] != i){
i = par[i];
}
return i;
}
bool cal(int a, int b, int D){
//printf("a:%d %d\n", num[a].x, num[a].y);
//printf("b:%d %d\n", num[b].x, num[b].y);
//printf("%.2f\n", pow(num[a].x - num[b].x, 2) + pow(num[a].y - num[b].y, 2));
if (pow(double(num[a].x - num[b].x), 2) + pow(double(num[a].y - num[b].y), 2) <= D*D){
return true;
}
else
return false;
}
void Link(int a, int b){
a = find(a);
b = find(b);
if (r[a] < r[b]){
par[a] = b;
}
else{
par[b] = a;
if (r[a] == r[b])
r[a]++;
}
}
int main(){
int N, D;
scanf("%d%d", &N, &D);
Init();
for (int i = 1; i <= N; i++){
scanf("%d%d", &num[i].x, &num[i].y);
}
char str[5];
while (~scanf("%s", str)){
int a, b;
if (str[0] == 'O'){
scanf("%d", &a);
for (int i = 0; i < tot; i++){
//printf("L %d %d\n", ok[i], a);
if (cal(ok[i], a, D)){
Link(ok[i], a);
//printf("Link %d %d\n", ok[i], a);
}
}
ok[tot++] = a;
}
else{
scanf("%d%d", &a, &b);
//printf("find %d %d\n", find(a), find(b));
if (find(a) == find(b)){
printf("SUCCESS\n");
}
else{
printf("FAIL\n");
}
}
}
return 0;
}