HDU-1558,Segment set,並查集+線段相交模擬
阿新 • • 發佈:2018-11-10
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
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; }