码迷,mamicode.com
首页 > 其他好文 > 详细

HDU--1558--Segment set--并查集***经典***

时间:2015-04-01 07:06:13      阅读:184      评论:0      收藏:0      [点我收藏+]

标签:

Segment set

Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3699    Accepted Submission(s): 1394


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.
 

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
 

Author
LL
 

Source
 

题意:二维坐标上给你n条小段,每次询问的时候求询问线段所有存在连接关系的线段的数量

这题很水。但是对于高中数学没学好或者像我这种遗忘到差不多的群体来说,太纠结了。

关键点在于证明两线段是否有连接关系,比如交叉,顶点相同,都算连接

由于遗忘的差不多了,就网上临时复习了向量积,花了不少时间才消化KO。。其实网上的都是标准说法,对于我们这种不喜欢看书的孩纸来说是天书,所以我把这抽象化的东东用日常一些的语言解释一遍。

假如两线段的两顶点分别是ab和cd,要么证明他们平行且有重叠,重叠就是说其中一线段有顶点在另一线段中。或者证明他们没有平行之后向量ab在向量ac和向量ad之间,同时向量ca在向量ca和向量cb之间,看图。。

技术分享

从途中可以看到,红色箭头是不符合cd在ca与cb中间的,这就是为什么要两个线段的端点都要取出来验证了,还有就是平行但是不共线,或者平行共线但是因为是线段不相连,所以不能算连接,下面说公式。。。

因为线段A(x1,y1,x2,y2)(x和y表示两个顶点的坐标)斜率L=(y2 - y1)/(x2 - x1)假设要求的是ac与ad的斜率差,那么就是Lac-Lad,然后把公式代入再展开就得到了我代码中dd函数里面的公式,它是用来求斜率差值的,而里面的cmp函数中用dd函数求了4次,这是因为要直接证明ab在ac和ad中间不简单,那么换一方案,直接比较斜率差值,就像上图中左下角的那个图,求出ac和ad的斜率差值还有bc和bd的斜率差值,如果你把线段的斜率用图画出来,你会发现两个差值相乘能判断ab和cd是否交叉,我画个图得了。。。

技术分享


这样就解决了ab与cd存在交叉但是ab太短了没办法从cd中间插过去的问题- -!确定插过去了是cmp函数中d1*d2和d3*d4都小于零的前提,d1*d2是保证ab插过cd,d3*d4是保证cd插过ab,咳咳咳,太邪恶了,baby你懂了没?

总述一下,先考虑平行问题,平行情况下是否有重叠,然后是可能存在交叉的问题,判断好ab从cd中间穿过且ab够长,同时cd对ab也要判断一次,这样就可以AC了

注意:数据类型要弄好,因为int会把double的小数位去掉,1.1会变成1,咳咳,太可恶了,就是这个WA我到死。。总之,这个题很经典,我是这么认为的


#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
struct node
{
    double x1,x2,y1,y2;
}s[1111];
int f[1111],num[1111];
double cc=1e-7;
int Find(int x)
{
    return f[x]=(f[x]==x?x:Find(f[x]));
}
void Union(int x,int y)
{
    x=Find(x);
    y=Find(y);
    if(x==y)return;//已经在一个集合中就不要重复了
    f[x]=y;
    num[y]+=num[x];//联系两个线段集合的时候顺便把集合中线段数量集中起来
}
double Max(double a,double b)
{
    return a>b?a:b;
}
double Min(double a,double b)
{
    return a<b?a:b;
}
double dd(double x1,double y1,double x2,double y2,double x3,double y3)//两线段的斜率差
{
    return (x2-x1)*(y3-y1)-(y2-y1)*(x3-x1);
}
int ddd(double x1,double y1,double x2,double y2,double x3,double y3)//共线情况下是否有重叠
{
    if(Max(x1,x2)>=x3&&Min(x1,x2)<=x3&&Max(y1,y2)>=y3&&Min(y1,y2)<=y3)
    return 1;
    return 0;
}
int cmp(node a,node b)
{
    double d1,d2,d3,d4;
    d1=dd(a.x1,a.y1,a.x2,a.y2,b.x1,b.y1);
    d2=dd(a.x1,a.y1,a.x2,a.y2,b.x2,b.y2);
    d3=dd(b.x1,b.y1,b.x2,b.y2,a.x1,a.y1);
    d4=dd(b.x1,b.y1,b.x2,b.y2,a.x2,a.y2);//cout<<d1<<d2<<" "<<d3<<d4<<endl;
    if(d1*d2<0&&d3*d4<0)return 1;//cout<<"X"<<endl;//斜率差一正一负可以得出两不共线线段交叉,上面有本人的个人看法说明
    if(d1==0&&ddd(a.x1,a.y1,a.x2,a.y2,b.x1,b.y1))return 1;//cout<<"A"<<endl;//共线的时候看看是否有重叠
    if(d2==0&&ddd(a.x1,a.y1,a.x2,a.y2,b.x2,b.y2))return 1;//cout<<"S"<<endl;
    if(d3==0&&ddd(b.x1,b.y1,b.x2,b.y2,a.x1,a.y1))return 1;//cout<<"D"<<endl;
    if(d4==0&&ddd(b.x1,b.y1,b.x2,b.y2,a.x2,a.y2))return 1;//cout<<"F"<<endl;
    return 0;
}
int main (void)
{
    int t,n,i,j,k,l;
    char c;
    scanf("%d",&t);
    while(t--&&scanf("%d",&n))
    {
        for(i=0;i<1111;i++)f[i]=i,num[i]=1;
        l=0;
        while(n--)
        {
            scanf("%*c%c",&c);
            if(c==‘P‘)
            {
                scanf("%lf%lf%lf%lf",&s[l].x1,&s[l].y1,&s[l].x2,&s[l].y2);
                for(i=0;i<l;i++)//与之前记录的所有线段匹配连接关系
                if(cmp(s[i],s[l]))//如果有连接
                /*cout<<"A"<<endl,*/Union(i,l);//并查集把线段联系起来
                l++;
            }else
            {
                scanf("%d",&k);
                printf("%d\n",num[Find(k-1)]);
            }
            //for(i=0;i<l;i++)cout<<num[i]<<" ";cout<<endl;
        }
        if(t)puts("");//妈啵的,这里没写PE了
    }
    return 0;
}

HDU--1558--Segment set--并查集***经典***

标签:

原文地址:http://blog.csdn.net/jingdianitnan/article/details/44795097

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!