1. 程式人生 > >HDU-1558,Segment set,並查集+線段相交模擬

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.
 
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;
}