SGU174 解题手记
  用并查集维护所有线段的端点,最初所有的端点都以自己为根,加入一条线段时,若两个端点在同一个集合中,则说明出现“封闭区域”,否则将一个端点的根设置为另外一个。并查集要有路径压缩。
  实现上,用map作为并查集的容器。
  Submit 1: WA on 2。合并操作写错了。“将一个端点的根设置为另外一个”是不准确的,应该是“将两个端点所在集合合并”。
  Submit 2: TLE on 4。常数太大了。
  Submit 3: TLE on 4。还是太大,用一个模板吧。
  Submit 4: PE on 4。What? 无解的判断写错了。
  Submit 5: AC。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <map>
using namespace std;
 
#define MAXN 400000
#define sig(x) ((x)>0?1:-1)
#define abs(x) ((x)>0?(x):-(x))
#define _ufind_run(x) for(;p[t=abs(x)];x=sig(x)*p[abs(x)],p[t]=sig(p[t])*(p[abs(x)]?p[abs(x)]:abs(p[t])))
#define _run_both _ufind_run(i);_ufind_run(j)
#define _set_side(x) p[abs(i)]=sig(i)*(abs(i)==abs(j)?0:(x)*j)
#define _judge_side(x) (i==(x)*j&&i)
 
struct ufind
{
    int p[MAXN],t;
    ufind(){memset(p,0,sizeof(p));}
    int set_friend(int i,int j){_run_both;_set_side(1);return !_judge_side(-1);}
    int set_enemy(int i,int j){_run_both;_set_side(-1);return !_judge_side(1);}
    int is_friend(int i,int j){_run_both;return _judge_side(1);}
    int is_enemy(int i,int j){_run_both;return _judge_side(-1);}
};
 
struct point
{
    int x,y;
};
 
bool operator <(const point &a,const point &b)
{
    return (a.x<b.x || a.x==b.x && a.y<b.y);
}
 
int main()
{
    map<point,int> points;
    ufind tland;
    int i,m;
    scanf("%d",&m);
    for (i=0;i<m;++i)
    {
        point a,b;
        scanf("%d%d%d%d",&a.x,&a.y,&b.x,&b.y);
        if (points.find(a)==points.end()) points[a]=i<<1;
        if (points.find(b)==points.end()) points[b]=(i<<1)+1;
        int ida=points[a],idb=points[b];
        if (tland.is_friend(ida,idb))
        {
            printf("%d\n",i+1); break;
        }
        tland.set_friend(ida,idb);
    }
    if (i>=m) printf("0\n");
    return 0;
}