HDU-1558,Segment set,并查集+线段相交模拟
Segment set
Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 5033 Accepted Submission(s): 1931
Problem Description
A segment and all segments which are connected with it compose a segment set. The size of a segment set is the number of segments in it. The problem is to find the size of some segment set.
Input
In the first line there is an integer t - the number of test case. For each test case in first line there is an integer n (n<=1000) - the number of commands.
There are two different commands described in different format shown below:
P x1 y1 x2 y2 - paint a segment whose coordinates of the two endpoints are (x1,y1),(x2,y2).
Q k - query the size of the segment set which contains the k-th segment.
k is between 1 and the number of segments in the moment. There is no segment in the plane at first, so the first command is always a P-command.
There are two different commands described in different format shown below:
P x1 y1 x2 y2 - paint a segment whose coordinates of the two endpoints are (x1,y1),(x2,y2).
Q k - query the size of the segment set which contains the k-th segment.
k is between 1 and the number of segments in the moment. There is no segment in the plane at first, so the first command is always a P-command.
Output
For each Q-command, output the answer. There is a blank line between test cases.
Sample Input
1
10
P 1.00 1.00 4.00 2.00
P 1.00 -2.00 8.00 4.00
Q 1
P 2.00 3.00 3.00 1.00
Q 1
Q 3
P 1.00 4.00 8.00 2.00
Q 2
P 3.00 3.00 6.00 -2.00
Q 5
Sample Output
1
2
2
2
5
并查集知识和模板见我的另一篇文章(用到了按数量合并)传送门
代码用了很多c++的封装技巧(类函数和运算符重载);重要内容都在代码上 注释了;
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cstring>
#define mem(x) memset(x,0,sizeof(x))
using namespace std;
const int N=1e3+1;
struct point{//点类
double x,y;
void reset(double a,double b){x=a;y=b;}
/* void show(){
cout<<"("<<x<<','<<y<<')'<<' ';
} */
};
struct seg{
point p1,p2;
double k;
void reset(double a,double b,double c,double d){
p1.reset(a,b);p2.reset(c,d);
k=dy(p1,p2)/dx(p1,p2);
}
double dx(point &a,point &b){
return a.x-b.x;
}
double dy(point &a,point &b){
return a.y-b.y;
}
double fun(point &a){ //将点带入直线,类似于线性规划的解法
return dy(a,p1)-k*dx(a,p1);
}
/* void show(){
p1.show();p2.show(); cout<<k<<endl;;
} */
};
bool is_connected(seg & a,seg &b){ //点在直线的两侧(一个点直线上也行)说明线段与直线相交,再反过来验证一遍;
//cout<<b.fun(a.p1)<<' '<<b.fun(a.p2)<<' '<<a.fun(b.p1)<<' '<<a.fun(b.p2)<<endl;
return (b.fun(a.p1)*b.fun(a.p2)<=0)&&(a.fun(b.p1)*a.fun(b.p2)<=0);
}
bool operator == (const point & a,const point &b){
return (a.x==b.x)&&(a.y==b.y);
}
bool operator == (const seg & a,const seg &b){
return (a.p1==b.p1)&&(a.p2==b.p2);
}
int fa[N];seg sg[N];
int f_r(int x){
int root=x,t;
while(fa[root]>0) root=fa[root];
while(x!=root){
t=fa[x]; //保留父亲节点
fa[x]=root;
x=t;
}
return x;
}
bool join(int r1,int r2){ //并集
if(r1==r2) return false;
if(fa[r1]<fa[r2]){
fa[r1]+=fa[r2]; //更新 后来发现这里写错了r1,r2是根,而fa[root]才是集合的个数的负值;
fa[r2]=r1; //合并
}
else{
fa[r2]+=fa[r1];
fa[r1]=r2;
}
return true;
}
/* void print(int n){cout<<"the fa is ";for(int i=1;i<=n;i++)cout<<fa[i]<<' ';cout<<endl;} */
int main(){
//freopen("in.txt","r",stdin);
int T;cin>>T;
while(T-- >0){
int n,t,cur=0;char op[2];double x1,y1,x2,y2;memset(fa,-1,sizeof(fa));
cin>>n;
while(n-- >0){
scanf("%s",op);
if(op[0]=='P'){
cin>>x1>>y1>>x2>>y2; //cout<<op<<' '<<x1<<' '<<y1<<" "<<x2<<' '<<y2<<endl;
cur++;
sg[cur].reset(x1,y1,x2,y2); //sg[cur].show();
for(int i=1;i<cur;i++){ //cout<<endl<<"i="<<i<<"cur="<<cur<<endl;
if(sg[i]==sg[cur]) {cur--;break;} //我这里特意判断了重复的线段
if (is_connected(sg[i],sg[cur]))
join(f_r(i),f_r(cur)); //并集
}
//print(cur);
}
else if(op[0]=='Q'){
cin>>t; //cout<<op<<' '<<t<<endl;
cout<<(-fa[f_r(t)])<<endl; //输出查询结点的根的绝对值即可;
}
}
if(T!=0)cout<<endl;
}
return 0;
}