どこでもドア:http://acm.hdu.edu.cn/showproblem.php?pid=1558
P:输入一条线段
Q:输出第k条线段有几个“朋友”(有几条连通的线段)
比较麻烦的是 判断两条线段是否相交。除去判断,这就是一个简单并查集题。
判断两条线段是否相交有好多算法。。我就copy了一个(copy的第一个有bug坑了我半天,所以copy的先验证)
CODE:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
#include<sstream>
#include<set>
#include<cstdlib>
#include<map>
#include<queue>
#include<fstream>
using namespace std;
typedef long long LL;
//const int INF = 0x3f3f3f3f;
const int M = 10005;
int t,n,sum,ma[M],fri[M];
struct EDG //用来记录边
{
double x1,y1,x2,y2;
}E[M];
/////判断两线段是否相交(来自copy)
///------------alg 2------------
//叉积
struct Point
{
double x, y;
};
double mult(Point a, Point b, Point c)
{
return (a.x-c.x)*(b.y-c.y)-(b.x-c.x)*(a.y-c.y);
}
//aa, bb为一条线段两端点 cc, dd为另一条线段的两端点 相交返回true, 不相交返回false
bool intersect(Point aa, Point bb, Point cc, Point dd)
{
if ( max(aa.x, bb.x)<min(cc.x, dd.x) )
{
return false;
}
if ( max(aa.y, bb.y)<min(cc.y, dd.y) )
{
return false;
}
if ( max(cc.x, dd.x)<min(aa.x, bb.x) )
{
return false;
}
if ( max(cc.y, dd.y)<min(aa.y, bb.y) )
{
return false;
}
if ( mult(cc, bb, aa)*mult(bb, dd, aa)<0 )
{
return false;
}
if ( mult(aa, dd, cc)*mult(dd, bb, cc)<0 )
{
return false;
}
return true;
}
///------------alg 2------------
/////
int find(int a){//找查结点所在哪个根结点
return a==ma[a]? a:ma[a]=find(ma[a]);
}
void mix(int a,int b){
a=find(a); b=find(b);
if(a!=b)
{
ma[a]=b; //用后来的结点做为根
fri[b]+=fri[a]; //记录根的朋友数
}
}
int main()
{
char c;
cin>>t;
while(t--)
{
cin>>n;
sum=1;
while(n--)
{
cin>>c;
if(c=='P'){
scanf("%lf%lf%lf%lf",&E[sum].x1,&E[sum].y1,&E[sum].x2,&E[sum].y2);
ma[sum]=sum;fri[sum]=1;//初始化集合
for(int i=1;i<sum;i++)
{
Point p1,p2,p3,p4;//仅仅为了把边的点提取出来好用copy来的的判断函数
p1.x=E[i].x1;p1.y=E[i].y1;
p2.x=E[i].x2;p2.y=E[i].y2;
p3.x=E[sum].x1;p3.y=E[sum].y1;
p4.x=E[sum].x2;p4.y=E[sum].y2;
if(intersect(p1,p2,p3,p4))
mix(ma[sum],ma[i]);
}
sum++;
}
if(c=='Q'){
int k;
cin>>k;
cout<<fri[find(k)]<<endl;
}
}
//格式
if(t)
cout<<endl;
}
return 0;
}