kuangbin带我飞QAQ 并查集

  1. POJ 2236

  给出N个点,一开始图是空白的,两个操作,一个是增加一个点(给出坐标),一个是查询两个点间是否相通,当两点间的距离小于D或者两点通过其他点间接相连时说这两个点相通。并查集维护,每次增加一个点加入到vector中并于之前的点比较,距离小于D则合并到一个集合即可。

 1 #include <iostream>
 2 #include <string.h>
 3 #include <cstdio>
 4 #include <vector>
 5 #include <string>
 6 #include <algorithm>
 7 #include <math.h>
 8 #include <time.h>
 9 
10 #define SIGMA_SIZE 26
11 #define lson rt<<1
12 #define rson rt<<1|1
13 #pragma warning ( disable : 4996 )
14 
15 using namespace std;
16 typedef long long LL;
17 inline LL LMax(LL a,LL b)      { return a>b?a:b; }
18 inline LL LMin(LL a,LL b)      { return a>b?b:a; }
19 inline int Max(int a,int b)    { return a>b?a:b; }
20 inline int Min(int a,int b)       { return a>b?b:a; }
21 inline int gcd( int a, int b ) { return b==0?a:gcd(b,a%b); }
22 inline int lcm( int a, int b ) { return a/gcd(a,b)*b; }  //a*b = gcd*lcm
23 const LL INF = 0x3f3f3f3f3f3f3f3f;
24 const LL mod  = 1000000007;
25 const double eps = 1e-8;
26 const int inf  = 0x3f3f3f3f;
27 const int maxk = 1005;
28 const int maxn = 1030;
29 
30 int N, D;
31 vector<int> alive;
32 bool vis[maxk];
33 int root[maxk];
34 int xi[maxk], yi[maxk];
35 
36 
37 int find( int x )
38 { return x==root[x] ? x : root[x]=find(root[x]); }
39 
40 void join( int x, int y )
41 {
42     x = find(x); y = find(y);
43     if ( x == y )
44         return;
45     root[x] = y;
46 }
47 
48 int getDist( int i, int j )
49 { return (xi[i]-xi[j])*(xi[i]-xi[j]) + (yi[i]-yi[j])*(yi[i]-yi[j]) ; }
50 
51 void init()
52 {
53     alive.clear();
54     memset(vis, 0, sizeof(vis) );
55 
56     for ( int i = 0; i < maxk; i++ )
57         root[i] = i;
58 
59     for ( int i = 1; i <= N; i++ )
60         scanf( "%d %d", &xi[i], &yi[i] );
61 }
62 
63 int main()
64 {
65     freopen("F:\\cpp\\test.txt", "r", stdin );
66     scanf("%d %d", &N, &D);
67     init();
68 
69     int x, y;
70     char str[5];
71     while ( ~scanf("%s", str) )
72     {
73         if ( str[0] == 'O' )
74         {
75             scanf("%d", &x);
76             vis[x] = true;
77             alive.push_back(x);
78 
79             for ( int i = 0; i < alive.size()-1; i++ )
80                 if ( getDist(alive[i], x) <= D*D )
81                     join(alive[i], x);
82         }
83         else
84         {
85             scanf("%d %d", &x, &y);
86             if ( (!vis[x]) || (!vis[y]) )
87                 printf("FAIL\n");
88             else
89             {
90                 x = find(x); y = find(y);
91                 if ( x == y )
92                     printf("SUCCESS\n");
93                 else
94                     printf("FAIL\n");
95             }
96         }
97     }
98 }
View Code

   2. HDU 1213

  并查集裸题,给N个点和M个关系,问最终有多少个集合

 1 #include <iostream>
 2 #include <string.h>
 3 #include <cstdio>
 4 #include <vector>
 5 #include <string>
 6 #include <algorithm>
 7 #include <math.h>
 8 #include <time.h>
 9 
10 #define SIGMA_SIZE 26
11 #define lson rt<<1
12 #define rson rt<<1|1
13 #pragma warning ( disable : 4996 )
14 
15 using namespace std;
16 typedef long long LL;
17 inline LL LMax(LL a,LL b)      { return a>b?a:b; }
18 inline LL LMin(LL a,LL b)      { return a>b?b:a; }
19 inline int Max(int a,int b)    { return a>b?a:b; }
20 inline int Min(int a,int b)       { return a>b?b:a; }
21 inline int gcd( int a, int b ) { return b==0?a:gcd(b,a%b); }
22 inline int lcm( int a, int b ) { return a/gcd(a,b)*b; }  //a*b = gcd*lcm
23 const LL INF = 0x3f3f3f3f3f3f3f3f;
24 const LL mod  = 1000000007;
25 const double eps = 1e-8;
26 const int inf  = 0x3f3f3f3f;
27 const int maxk = 1005;
28 const int maxn = 1030;
29 
30 int N, M;
31 bool vis[maxk];
32 int root[maxk];
33 
34 int find( int x )
35 { return x==root[x] ? x : root[x]=find(root[x]); }
36 
37 void join( int x, int y )
38 {
39     x = find(x); y = find(y);
40     if ( x == y )
41         return;
42     root[x] = y;
43 }
44 
45 void init()
46 {
47     memset( vis, 0, sizeof(vis) );
48     for ( int i = 0; i < maxk; i++ )
49         root[i] = i;
50 }
51 
52 int main()
53 {
54     //freopen("F:\\cpp\\test.txt", "r", stdin );
55     
56     int T; cin >> T;
57     while (T--)
58     {
59         int cnt = 0;
60         int x, y;
61 
62         init();
63         scanf("%d %d", &N, &M);
64 
65         for ( int i = 1; i <= M; i++ )
66         {
67             scanf("%d %d", &x, &y);
68             join(x,y);
69         }
70 
71         for ( int i = 1; i <= N; i++ )
72             vis[find(i)] = true;
73         for ( int i = 1; i <= N; i++ )
74             if (vis[i])
75                 cnt++;
76         printf("%d\n", cnt);
77     }
78 
79     return 0;
80 }
View Code

   3.HDU 3038 

  和一道经典的题目(就是下一道)食物链很像,记得某场比赛做过类似的,当时以为是线段树各种做不出,,,结果发现原来是并查集

  给一组数列,数字未知,给出A,B和[A,B]表示A到B这段子数列内的数字和,这样按顺序给出来,问你有多少个逻辑错误,遇到错误则跳过该错误。

  比如给了[1,4] = 5, [1,2] = 3,这个时候再给一个[3,4]=6,那么这一行明显和前面得到的条件矛盾,所以错误数加一并且跳过这一行,具体是用向量操作,可以参考这两篇博客:

  https://www.cnblogs.com/liyinggang/p/5327055.html

  https://blog.csdn.net/niushuai666/article/details/6981689

 1 #include <iostream>
 2 #include <string.h>
 3 #include <cstdio>
 4 #include <vector>
 5 #include <string>
 6 #include <algorithm>
 7 #include <math.h>
 8 #include <time.h>
 9 
10 #define SIGMA_SIZE 26
11 #define lson rt<<1
12 #define rson rt<<1|1
13 #pragma warning ( disable : 4996 )
14 
15 using namespace std;
16 typedef long long LL;
17 inline LL LMax(LL a,LL b)      { return a>b?a:b; }
18 inline LL LMin(LL a,LL b)      { return a>b?b:a; }
19 inline int Max(int a,int b)    { return a>b?a:b; }
20 inline int Min(int a,int b)       { return a>b?b:a; }
21 inline int gcd( int a, int b ) { return b==0?a:gcd(b,a%b); }
22 inline int lcm( int a, int b ) { return a/gcd(a,b)*b; }  //a*b = gcd*lcm
23 const LL INF = 0x3f3f3f3f3f3f3f3f;
24 const LL mod  = 1000000007;
25 const double eps = 1e-8;
26 const int inf  = 0x3f3f3f3f;
27 const int maxk = 1005;
28 const int maxn = 2e5+5;
29 
30 int root[maxn];
31 int sum[maxn];
32 
33 int _find( int x )
34 {
35      int fa = root[x];
36 
37      if ( x == root[x] )
38          return x;
39 
40     root[x] = _find(fa);
41     sum[x] += sum[fa];
42 
43     return root[x];
44 }
45 
46 void init( int n )
47 {
48     for ( int i = 0; i <= n; i++ )
49     {
50         root[i] = i;
51         sum[i] = 0;
52     }
53 }
54 
55 int main()
56 {
57     //freopen("F:\\cpp\\test.txt", "r", stdin );
58 
59     int N, M, cnt;
60     while (cin >> N >> M)
61     {
62         init(N);
63 
64         cnt = 0;
65         int x, y, val;
66         int xr, yr;
67         while ( M-- )
68         {
69             scanf("%d %d %d", &x, &y, &val);
70             
71             x--;
72             xr = _find(x); yr = _find(y);
73             if ( xr == yr )
74             {
75                 if ( val != sum[x] - sum[y] )
76                     cnt++;
77             }
78             else
79             {
80                 root[xr] = yr;
81                 sum[xr] = sum[y]-sum[x]+val;
82             }
83         }
84 
85         printf("%d\n", cnt);
86     }
87 
88     return 0;
89 }
View Code

   4. HDU 1272

  POJ日常抽风,只能跑来做HDU的题目了233,实际上就是给定一个图让你判断是不是一颗连通树,所以有两个条件:无环,连通。无环用并查集判断,如果有两个点都在同一并查集,则说明肯定有环了。连通可以用(在无环的情况下)点数=边数+1判断,也可以用最后算出来总共只有一个集合来判断。坑爹的是要考虑空树,也就是直接给0 0的情况...

  1 #include <iostream>
  2 #include <string.h>
  3 #include <cstdio>
  4 #include <vector>
  5 #include <set>
  6 #include <string>
  7 #include <algorithm>
  8 #include <math.h>
  9 #include <time.h>
 10 
 11 #define SIGMA_SIZE 26
 12 #define lson rt<<1
 13 #define rson rt<<1|1
 14 #pragma warning ( disable : 4996 )
 15 
 16 using namespace std;
 17 typedef long long LL;
 18 inline LL LMax(LL a,LL b)      { return a>b?a:b; }
 19 inline LL LMin(LL a,LL b)      { return a>b?b:a; }
 20 inline int Max(int a,int b)    { return a>b?a:b; }
 21 inline int Min(int a,int b)       { return a>b?b:a; }
 22 inline int gcd( int a, int b ) { return b==0?a:gcd(b,a%b); }
 23 inline int lcm( int a, int b ) { return a/gcd(a,b)*b; }  //a*b = gcd*lcm
 24 const LL INF = 0x3f3f3f3f3f3f3f3f;
 25 const LL mod  = 1000000007;
 26 const double eps = 1e-8;
 27 const int inf  = 0x3f3f3f3f;
 28 const int maxk = 1005;
 29 const int maxn = 1e5+5;
 30 
 31 
 32 int root[maxn];
 33 set<int> num;
 34 
 35 int _find(  int x )  
 36 { return x==root[x] ? x : root[x]=_find(root[x]); }  
 37 
 38 void merge( int x, int y )
 39 {
 40     x = _find(x); y = _find(y);
 41     if ( x == y )
 42         return;
 43     root[x] = y;
 44 }
 45 
 46 void init()
 47 {
 48     num.clear();
 49     for ( int i = 0; i < maxn; i++ )
 50         root[i] = i;
 51 }
 52 
 53 int main()
 54 {
 55     //freopen("F:\\cpp\\test.txt", "r", stdin );
 56 
 57     int x, y;
 58     while (1)
 59     {
 60         int cnt = 0;
 61         bool ok = true;
 62         init();
 63 
 64         scanf("%d %d", &x, &y);
 65         if ( x == -1 ) 
 66             break;
 67         if ( x == 0 )
 68         {
 69             printf("Yes\n");
 70             continue;
 71         }
 72         merge(x,y);
 73         num.insert(x); num.insert(y);
 74         cnt++;
 75 
 76         while (1)
 77         {
 78             scanf("%d %d", &x, &y);
 79             if ( x == 0 ) 
 80                 break;
 81             num.insert(x); num.insert(y);
 82             cnt++;
 83 
 84             if (ok)
 85             {
 86                 x = _find(x); y = _find(y);
 87                 if ( x == y )
 88                     ok = false;
 89 
 90                 merge(x, y);
 91             }
 92         }
 93 
 94         if ( ok && num.size() == cnt+1 )
 95             printf("Yes\n");
 96         else
 97             printf("No\n");
 98     }
 99     return 0;
100 }
View Code

   

  5. ZOJ 3261

  思路简单,写起来麻烦...大意是给N个点M条边,每个点有一个power值。然后有两个操作,一是毁灭其中一条边,二是询问一个点A,求与A直接或间接相连的所有点中power值最大的那个,且求出来的power值需大于A的power值,当有多个点power值相同,取序号最小那个。

  显然并查集不支持删除,所以需要逆向操作,先按顺序存储所有操作,做出删完所有该删的边的最终图,再从后往前加边,询问。

  询问操作比较好存,毁灭操作就难一点了,这里我用了map,因为map搜索是log级别的,即使要搜两边(x y和y x)也很快。其他除了并查集merge的时候要注意点就没什么了,就是写的好慢...

  1 #include <iostream>
  2 #include <string.h>
  3 #include <cstdio>
  4 #include <vector>
  5 #include <map>
  6 #include <string>
  7 #include <algorithm>
  8 #include <math.h>
  9 #include <time.h>
 10 
 11 #define SIGMA_SIZE 26
 12 #define lson rt<<1
 13 #define rson rt<<1|1
 14 #pragma warning ( disable : 4996 )
 15 
 16 using namespace std;
 17 typedef long long LL;
 18 inline LL LMax(LL a,LL b)      { return a>b?a:b; }
 19 inline LL LMin(LL a,LL b)      { return a>b?b:a; }
 20 inline int Max(int a,int b)    { return a>b?a:b; }
 21 inline int Min(int a,int b)       { return a>b?b:a; }
 22 inline int gcd( int a, int b ) { return b==0?a:gcd(b,a%b); }
 23 inline int lcm( int a, int b ) { return a/gcd(a,b)*b; }  //a*b = gcd*lcm
 24 const LL INF = 0x3f3f3f3f3f3f3f3f;
 25 const LL mod  = 1000000007;
 26 const double eps = 1e-8;
 27 const int inf  = 0x3f3f3f3f;
 28 const int maxk = 1005;
 29 const int maxn = 1e4+5;
 30 
 31 //struct edge {
 32 //    int to, next;
 33 //}e[4*maxn];
 34 
 35 map<pair<int,int>,bool> mmap;
 36 map<pair<int,int>,bool>::iterator itor;
 37 
 38 int xi[5*maxn], yi[5*maxn], query[5*maxn];
 39 int root[maxn], val[maxn];
 40 int N, M, Q, death_num;
 41 
 42 
 43 //void addedge( int u, int v )
 44 //{ e[cnt].to = v; e[cnt].next = linjie[u]; linjie[u] = cnt++; }
 45 
 46 int _find( int x )
 47 { return x==root[x] ? x : root[x]=_find(root[x]); }
 48 
 49 void merge( int x, int y )
 50 {
 51     x = _find(x); y = _find(y);
 52     if ( x == y )
 53         return;
 54 
 55     //让 x < y
 56     if ( x > y ) swap(x,y);
 57 
 58     if ( val[x] > val[y] ) root[y] = x;
 59     else if ( val[x] < val[y] ) root[x] = y;
 60     else root[y] = x;
 61 }
 62 
 63 void init( int n )
 64 {
 65     death_num = 0; 
 66     mmap.clear();
 67 
 68     //memset( linjie, -1, sizeof(linjie) );
 69     for ( int i = 0; i <= n; i++ )
 70         root[i] = i;
 71 }
 72 
 73 void read( int n )
 74 {
 75     //读入power
 76     for ( int i = 0; i < n; i++ )
 77         scanf("%d", &val[i]);
 78     
 79     //读入边
 80     cin >> M;
 81     int x, y;
 82     for ( int i = 0; i < M; i++ )
 83     {
 84         scanf("%d %d", &x, &y);
 85         mmap.insert( make_pair(make_pair(x,y), true) );
 86     }
 87 
 88     //读入query
 89     cin >> Q;
 90     char str[10];
 91     for ( int i = 0; i < Q; i++ )
 92     {
 93         scanf("%s", str);
 94         if ( str[0] == 'q' )
 95         {
 96             scanf("%d", &x);
 97             query[i] = x;
 98         }
 99         else
100         {
101             //依次读入被毁灭的边,然后在mmap中寻找并变为false
102             scanf("%d %d", &xi[death_num], &yi[death_num]);
103             itor = mmap.find(make_pair(xi[death_num],yi[death_num]));
104             if ( itor != mmap.end() )
105                 (*itor).second = false;
106             else
107             {
108                 itor = mmap.find(make_pair(yi[death_num],xi[death_num]));
109                 (*itor).second = false;
110             }
111 
112             death_num++;
113             query[i] = -2;
114         }
115     }
116 }
117 
118 void make_map()
119 {
120     int x, y;
121 
122     //如果该边没有被毁灭
123     for ( itor = mmap.begin(); itor != mmap.end(); itor++ )
124         if ( (*itor).second )
125         {
126             x = ((*itor).first).first; y = ((*itor).first).second;
127             //addedge( x, y );
128             //addedge( y, x );
129             merge( x, y );
130         }
131 }
132 
133 void solve()
134 {
135     int tmp;
136     for ( int i = Q-1; i >= 0; i-- )
137     {    
138                                         
139         if ( query[i] != -2 )                //如果是询问
140         {
141             tmp = _find(query[i]);
142             if ( val[tmp] <= val[query[i]] )
143                 query[i] = -1;
144             else
145                 query[i] = tmp;
146         }
147         else
148         {
149             death_num--;
150             merge(xi[death_num], yi[death_num]);
151         }
152     }
153 }
154 
155 int main()
156 {
157     //freopen("F:\\cpp\\test.txt", "r", stdin );
158 
159     int cnt = 0;
160     while ( ~scanf("%d", &N) )
161     {
162         if ( cnt )
163             printf("\n");
164         init(N);
165         read(N);
166         make_map();
167         solve();
168         for ( int i = 0; i < Q; i++ )
169             if ( query[i] != -2)
170                 printf("%d\n", query[i]);
171         cnt++;
172     }
173 
174     return 0;
175 }
View Code

   

  6. POJ 2492

  并查集做这种题实在太好了,这道题像是食物链的弱化版,给N个虫子,只有异性间才会交配.....给M个交配方案,看看虫子里面有没有同性恋(笑)这个关系还是比较简单的,用0表示两虫子同性,1表示虫子异性,rela[i]表示i和根节点的关系。

三个虫子中有三种关系(A-B, B-C, C-A)只要知道其中两种,很容易推出第三种,然后推出的关系维护并查集,具体看代码,很简单

  1 #include <iostream>
  2 #include <string.h>
  3 #include <cstdio>
  4 #include <vector>
  5 #include <map>
  6 #include <string>
  7 #include <algorithm>
  8 #include <math.h>
  9 #include <time.h>
 10 
 11 #define SIGMA_SIZE 26
 12 #define lson rt<<1
 13 #define rson rt<<1|1
 14 #pragma warning ( disable : 4996 )
 15 
 16 using namespace std;
 17 typedef long long LL;
 18 inline LL LMax(LL a,LL b)      { return a>b?a:b; }
 19 inline LL LMin(LL a,LL b)      { return a>b?b:a; }
 20 inline int Max(int a,int b)    { return a>b?a:b; }
 21 inline int Min(int a,int b)       { return a>b?b:a; }
 22 inline int gcd( int a, int b ) { return b==0?a:gcd(b,a%b); }
 23 inline int lcm( int a, int b ) { return a/gcd(a,b)*b; }  //a*b = gcd*lcm
 24 const LL INF = 0x3f3f3f3f3f3f3f3f;
 25 const LL mod  = 1000000007;
 26 const double eps = 1e-8;
 27 const int inf  = 0x3f3f3f3f;
 28 const int maxk = 1e6+5;
 29 const int maxn = 2005;
 30 
 31 //rela[i]表示i与其根的关系 为0表示同性,为1表示异性
 32 int root[maxn];
 33 bool rela[maxn];
 34 int N, Q;
 35 
 36 void init()
 37 {
 38     scanf("%d %d", &N, &Q);
 39 
 40     memset( rela, 0, sizeof(rela) );
 41     for ( int i = 0; i <= N; i++ )
 42         root[i] = i;
 43 }
 44 
 45 bool getRela( bool master, bool minor )
 46 {
 47     //如果两者关系为异性,则第三条关系相反
 48     if ( master )
 49         return (!minor);
 50     else
 51         return minor;
 52 }
 53 
 54 int _find( int x )
 55 { 
 56     int r = x, pre = root[x];
 57     if ( r == root[r] )
 58         return root[r];
 59 
 60     r = _find(root[r]);
 61     root[x] = r;
 62     rela[x] = getRela( rela[x], rela[pre] );
 63 
 64     return root[x];
 65 }
 66 
 67 void merge( int x, int y, int xr, int yr )
 68 {
 69     //tmp是y和xr的关系
 70     bool tmprela = getRela( true, rela[x] );
 71     root[xr] = yr;
 72     rela[xr] = getRela( tmprela, rela[y] );
 73 }
 74 
 75 int main()
 76 {
 77     //freopen("F:\\cpp\\test.txt", "r", stdin );
 78 
 79     int T; cin >> T;
 80     int CNT = 1;
 81     while (T--)
 82     {
 83         bool nobug = true;
 84         int x, y, xr, yr;
 85 
 86         init();
 87         while (Q--)
 88         {
 89             scanf("%d %d", &x, &y);
 90             if (nobug)
 91             {
 92                 xr = _find(x); yr = _find(y);
 93                 if ( xr == yr )
 94                 {
 95                     if ( rela[x] != getRela(true, rela[y]) )
 96                         nobug = false;
 97                 }
 98                 else
 99                     merge(x, y, xr, yr);
100             }
101         }
102         printf("Scenario #%d:\n", CNT++);
103         if ( nobug )
104             printf("No suspicious bugs found!\n");
105         else
106             printf("Suspicious bugs found!\n");
107         printf("\n");
108     }
109 
110     return 0;
111 }
View Code

   

  7.POJ 1182

  食物链,经典题目,有了前面的铺垫这道题就算是硬算也不难,前面几题集合中只有两种关系,这里就有三种关系,本质上是向量+偏移量,懂了还是不难的

  PS:这道题只能单次输入,如果加了while循环输入就会WA...完全不知道为什么,害我查了一个小时的bug......

 1 #include <iostream>
 2 #include <string.h>
 3 #include <cstdio>
 4 #include <vector>
 5 #include <map>
 6 #include <string>
 7 #include <algorithm>
 8 #include <math.h>
 9 #include <time.h>
10 
11 #define SIGMA_SIZE 26
12 #define lson rt<<1
13 #define rson rt<<1|1
14 #pragma warning ( disable : 4996 )
15 
16 using namespace std;
17 typedef long long LL;
18 inline LL LMax(LL a,LL b)      { return a>b?a:b; }
19 inline LL LMin(LL a,LL b)      { return a>b?b:a; }
20 inline int Max(int a,int b)    { return a>b?a:b; }
21 inline int Min(int a,int b)       { return a>b?b:a; }
22 inline int gcd( int a, int b ) { return b==0?a:gcd(b,a%b); }
23 inline int lcm( int a, int b ) { return a/gcd(a,b)*b; }  //a*b = gcd*lcm
24 const LL INF = 0x3f3f3f3f3f3f3f3f;
25 const LL mod  = 1000000007;
26 const double eps = 1e-8;
27 const int inf  = 0x3f3f3f3f;
28 const int maxk = 1e6+5;
29 const int maxn = 5e4+5;
30 
31 int root[maxn], rela[maxn];
32 
33 int _find( int x )
34 {
35     if ( x == root[x] )
36         return x;
37      
38     int pre = root[x];
39     root[x] = _find(root[x]);
40     rela[x] = (rela[x] + rela[pre]) % 3;
41 
42     return root[x];
43 }
44 
45 int main()
46 {
47     //freopen("F:\\cpp\\test.txt", "r", stdin );
48     int N, K, cnt;
49     scanf("%d %d", &N, &K);
50     for ( int i = 0; i <= N; i++ )
51         root[i] = i;
52     cnt = 0;
53     memset( rela, 0, sizeof(rela) );
54 
55         
56     int d, x, y, xr, yr;
57     while (K--)
58     {
59         scanf("%d %d %d", &d, &x, &y);
60         d--;
61 
62         if ( x > N || y > N )
63             { cnt++; continue; }
64         if ( x == y && d == 1 )
65             { cnt++; continue; }
66 
67         xr = _find(x); yr = _find(y);
68         if ( xr == yr )
69         {
70             if ( d != ((rela[x] - rela[y] + 3)%3) )
71                 cnt++;
72         }
73         else
74         {
75             root[xr] = yr;
76             rela[xr] = (rela[y] + d - rela[x] + 3) % 3;
77         }
78     }
79     printf("%d\n", cnt);
80 
81     return 0;
82 }
View Code

   

  8.POJ 1611 The Suspects 

  并查集裸题,给M个组,每个组有N个学生,其中0号学生是感染者,和感染者同一组的都是感染者,所以只要求0号学生为根的集合的学生个数即可,merge时约定0号学生为根就可以了。

 1 #include <iostream>
 2 #include <string.h>
 3 #include <cstdio>
 4 #include <vector>
 5 #include <map>
 6 #include <string>
 7 #include <algorithm>
 8 #include <math.h>
 9 #include <time.h>
10 
11 #define SIGMA_SIZE 26
12 #define lson rt<<1
13 #define rson rt<<1|1
14 #pragma warning ( disable : 4996 )
15 
16 using namespace std;
17 typedef long long LL;
18 inline LL LMax(LL a,LL b)      { return a>b?a:b; }
19 inline LL LMin(LL a,LL b)      { return a>b?b:a; }
20 inline int Max(int a,int b)    { return a>b?a:b; }
21 inline int Min(int a,int b)       { return a>b?b:a; }
22 inline int gcd( int a, int b ) { return b==0?a:gcd(b,a%b); }
23 inline int lcm( int a, int b ) { return a/gcd(a,b)*b; }  //a*b = gcd*lcm
24 const LL INF = 0x3f3f3f3f3f3f3f3f;
25 const LL mod  = 1000000007;
26 const double eps = 1e-8;
27 const int inf  = 0x3f3f3f3f;
28 const int maxk = 3e4+5;
29 const int maxn = 5e4+5;
30 
31 int root[maxk], num[maxk];
32 int N, M;
33 
34 void init()
35 {
36     for ( int i = 0; i <= N; i++ )
37     {
38         root[i] = i;
39         num[i] = 1;
40     }
41 }
42 
43 int _find(int x)
44 { return x==root[x] ? x : root[x] = _find(root[x]); }
45 
46 void merge( int x, int y )
47 {
48     int xr = _find(x);
49     int yr = _find(y);
50 
51     if (xr != yr)
52     {
53         if (xr == 0)
54         {
55             root[yr] = xr;
56             num[xr] = num[xr] + num[yr];
57         }
58         else
59         {
60             root[xr] = yr;
61             num[yr] = num[xr]+num[yr];
62         }
63     }
64 }
65 
66 int main()
67 {
68     while ( ~scanf("%d %d", &N, &M) )
69     {
70         if ( N == 0 && M == 0 )
71             break;
72 
73         init();
74 
75         int x;
76         for ( int j = 0; j < M; j++ )
77         {
78             int a, b;
79 
80             scanf("%d", &x);
81             scanf("%d", &a);
82             if ( x != 1 )
83                 for (int i = 1; i <= x-1; i++)
84                 {
85                     scanf("%d", &b);
86                     merge(a, b);
87                     a = b;
88 
89                 }
90         }
91 
92         printf("%d\n", num[0]);
93     }
94 
95     return 0;
96 }
View Code

   

  9. POJ 1417 True Liars

  村子里面有p1个好人p2个坏人,好人只会说真话,坏人只会说假话,现在可以问N个问题, 每个问题就是随机挑两人a和b,让a回答b是不是好人,然后让你判断出所有的好人。

  如果这道题只是求有没有矛盾,那就是一道很简单的带权并查集,和前面那个同性恋虫子思路一样。但现在问题是要求出所有好人,显然只根据问的N个问题是求不出来的,因为如果没有第一个能被确定为好人或者坏人的人,那么剩下所有人都是一个相对的关系,这样只能推断两个人是同为好人还是同为坏人或者一好一坏。经过简单的推算发现回答为yes时a和b为同类人,no时a与b异类,开个rela[i]数组表示i与根节点是同类还是异类, 这样带权并查集直接走起。

  那么我们可以用并查集先求出几个大集合,并且记录每个大集合A类人和B类人的个数,当然我们现在并不知道A类是好人还是坏人,只是一个相对的关系。显而易见我们应该在每个大集合各中抽取一个A类人或者B类人组成好人集合,如果好人集合人数正好为p1,并且这种选法唯一那我们就能确定所有好人了。比如只有一个大集合有4个A类人和4个B类人,而好人只有4个,那我们就不知道到底是A类是好人还是B类是好人了。实际上就像一个组合问题,看能否形成唯一的组合使其总数正好为p1,这点用dp很简单就能实现,dp[i][j]表示第i个大集合中使人数为j的方法最多有多少种,最后只要看dp[cnt][p1]是否等于1就可以了。

  然而这题最麻烦的还是记录路径,有一种方法是如果从dp[cnt][p1]往前推。因为dp[i][j]必然是由dp[i-1][j-A]和dp[i-1][j-B](A, B分别为某个大集合A类人和B类人的个数)推过来的,这两个dp只有一个为1,不然dp[i][j]就等于2了(有两种路径可以使好人人数达到 j ),所以只要将p1一步一步减回去看dp为1的那一类就是好人类。

  1 #include <iostream>
  2 #include <string.h>
  3 #include <cstdio>
  4 #include <vector>
  5 #include <map>
  6 #include <string>
  7 #include <algorithm>
  8 #include <time.h>
  9 
 10 #define SIGMA_SIZE 26
 11 #define lson rt<<1
 12 #define rson rt<<1|1
 13 #pragma warning ( disable : 4996 )
 14 
 15 using namespace std;
 16 typedef long long LL;
 17 inline LL LMax(LL a,LL b)      { return a>b?a:b; }
 18 inline LL LMin(LL a,LL b)      { return a>b?b:a; }
 19 inline int Max(int a,int b)    { return a>b?a:b; }
 20 inline int Min(int a,int b)       { return a>b?b:a; }
 21 inline int gcd( int a, int b ) { return b==0?a:gcd(b,a%b); }
 22 inline int lcm( int a, int b ) { return a/gcd(a,b)*b; }  //a*b = gcd*lcm
 23 const LL INF = 0x3f3f3f3f3f3f3f3f;
 24 const LL mod  = 1000000007;
 25 const double eps = 1e-8;
 26 const int inf  = 0x3f3f3f3f;
 27 const int maxk = 3e4+5;
 28 const int maxn = 600;
 29 
 30 
 31 int n, p1, p2, cnt;
 32 //num[i][0]表示该集合中与根节点i种类相同的人个数,反正亦然
 33 int num[602][2], root[602];
 34 //dp[i][j]表示前i的个大集合中天使人数为j的集合最多多少个
 35 int dp[602][602];
 36 int divide[602][2];
 37 bool rela[602]; //false表示与根为同类
 38 
 39 vector<int> ans, _list;
 40 vector<int> v0[602], v1[602];
 41 
 42 void init()
 43 {
 44     cnt = 1; ans.clear(); _list.clear();
 45     memset(num, 0, sizeof(num));
 46     memset(rela, 0, sizeof(rela));
 47     memset(dp, 0, sizeof(dp));
 48     for ( int i = 0; i <= p1+p2; i++ )
 49     {
 50         root[i] = i;
 51         num[i][0] = 1;
 52     }
 53 }
 54 
 55 int _find( int x )
 56 {
 57     if (x == root[x])
 58         return root[x];
 59 
 60     int pre = _find(root[x]);
 61 
 62     if (rela[root[x]])
 63         rela[x] = !rela[x];
 64     root[x] = pre;
 65 
 66     return pre;
 67 }
 68 
 69 void merge( int x, int y, bool link )
 70 {
 71     int xr = _find(x), yr = _find(y);
 72     if (xr == yr) 
 73         return;
 74 
 75     if (rela[x])
 76         link = !link;
 77     if (rela[y])
 78         rela[xr] = !link;
 79     else 
 80         rela[xr] = link;
 81 
 82     root[xr] = yr;
 83 
 84     //如果xr与yr是同类,则yr组中同类的数目要加上xr组中与xr同类的个数
 85     if (!rela[xr])
 86     {
 87         num[yr][0] += num[xr][0];
 88         num[yr][1] += num[xr][1];
 89     }
 90     else
 91     {
 92         num[yr][0] += num[xr][1];
 93         num[yr][1] += num[xr][0];
 94     }
 95 }
 96 
 97 void solve()
 98 {
 99     _list.push_back(0);
100     for ( int i = 1; i <= p1+p2; i++ )
101     {
102         if ( _find(i) == i )
103         {
104             divide[cnt][0] = num[i][0];
105             divide[cnt][1] = num[i][1];
106             _list.push_back(i);
107             cnt++;
108         }
109     }
110 
111     for ( int i = 1; i <= p1+p2; i++ )
112     {
113         int rt = root[i];
114         if ( !rela[i] )
115             v0[rt].push_back(i);
116         else
117             v1[rt].push_back(i);
118     }
119 
120     //处理DP
121     dp[0][0] = 1;
122     for (int i = 1; i < cnt; i++)
123     {
124         int mmin = Min(divide[i][0], divide[i][1]);
125         for (int j = p1; j >= mmin; j--)
126         {
127             dp[i][j] += dp[i-1][j-divide[i][0]];
128             dp[i][j] += dp[i-1][j-divide[i][1]];
129         }
130     }
131 
132 
133     if ( dp[cnt-1][p1] == 1 )
134     {
135         int tmp1 = p1, tmp2 = p2;
136         for ( int i = cnt-1; i >= 1; i-- )
137         {
138             if ( tmp1-divide[i][0]>=0 && tmp2-divide[i][1]>=0 && dp[i-1][tmp1-divide[i][0]]==1 )
139             {
140                 int size = v0[_list[i]].size();
141                 for ( int j = 0; j < size; j++ )
142                     ans.push_back(v0[_list[i]][j]);
143                 tmp1 -= divide[i][0];
144                 tmp2 -= divide[i][1];
145             }
146             else if ( tmp1-divide[i][1]>=0 && tmp2-divide[i][0]>=0 && dp[i-1][tmp1-divide[i][1]]==1 )
147             {
148                 int size = v1[_list[i]].size();
149                 for ( int j = 0; j < size; j++ )
150                     ans.push_back(v1[_list[i]][j]);
151                 tmp1 -= divide[i][1];
152                 tmp2 -= divide[i][0];
153             }
154 
155             v0[_list[i]].clear();
156             v1[_list[i]].clear();
157         }
158         sort(ans.begin(), ans.end());
159         for ( int i = 0; i < ans.size(); i++ )
160             printf("%d\n", ans[i]);
161         printf("end\n");
162     }
163     else
164     {
165         printf("no\n");
166         for ( int i = 1; i < _list.size(); i++ )
167         {
168             v0[_list[i]].clear();
169             v1[_list[i]].clear();
170         }
171     }
172 }
173 
174 int main()
175 {
176     while ( ~scanf("%d %d %d", &n, &p1, &p2) && n+p1+p2 )
177     {
178         init();
179         int x, y;
180         char str[5];
181         bool link = false;    //false表示x与y是同类
182 
183         while (n--)
184         {
185             scanf("%d %d", &x, &y); scanf("%s", str);
186             if (str[0] == 'n') link = true;
187             else link = false;
188 
189             merge(x, y, link);
190         }
191 
192         solve();
193     }
194 
195     return 0;
196 }
View Code

   

  10. POJ 1733

  看上去是一道简单的种类并查集,但是关系并不好找,在网上看到一大神的思路,遂跪。

  对于[l,r],我们可以简单的设sum[l-1],sum[r]为0~l-1,0~r的前缀,这和前面种类并查集是一样的,然而这道题只给了你[l,r]之间1的个数为奇数或者偶数,这我半天都没反应过来怎么转换。其实可以这样想,如果[l,r]之间1个数为奇数,那么sum[l-1]^sum[r]就应该等于1,因为可以把sum[r]拆为0~l-1和l~r这两端,前面一段和sum[l-1]异或就变成零了,后面一段因为有奇数个1所以异或结果为1,同理如果[l,r]间1为偶数,那么最后就应该等于0。但这道题我们并不需要存sum,我们只要一个rela数组存奇偶关系, 所以每条向量(假设向量两个端点为x,y)的rela值就应该等于sum[x]^sum[y]。令三角向量三个端点为l-1, r, root,稍微推一下就知道怎么写了。

  1 #include <iostream>
  2 #include <string.h>
  3 #include <cstdio>
  4 #include <vector>
  5 #include <map>
  6 #include <string>
  7 #include <algorithm>
  8 #include <time.h>
  9 
 10 #define SIGMA_SIZE 26
 11 #define lson rt<<1
 12 #define rson rt<<1|1
 13 #pragma warning ( disable : 4996 )
 14 
 15 using namespace std;
 16 typedef long long LL;
 17 inline LL LMax(LL a,LL b)      { return a>b?a:b; }
 18 inline LL LMin(LL a,LL b)      { return a>b?b:a; }
 19 inline int Max(int a,int b)    { return a>b?a:b; }
 20 inline int Min(int a,int b)       { return a>b?b:a; }
 21 inline int gcd( int a, int b ) { return b==0?a:gcd(b,a%b); }
 22 inline int lcm( int a, int b ) { return a/gcd(a,b)*b; }  //a*b = gcd*lcm
 23 const LL INF = 0x3f3f3f3f3f3f3f3f;
 24 const LL mod  = 1000000007;
 25 const double eps = 1e-8;
 26 const int inf  = 0x3f3f3f3f;
 27 const int maxn = 2e4+5;
 28 
 29 
 30 //1个数为偶数则为0
 31 int rela[maxn];
 32 int root[maxn], num[maxn];
 33 int len, N, cnt, n;
 34 
 35 struct node {
 36     int lhs, rhs, rela;
 37 }lisan[maxn];
 38 
 39 int _find(int x)
 40 {
 41     if ( x == root[x] ) return root[x];
 42     int pre = _find(root[x]);
 43     rela[x] = rela[x]^rela[root[x]];
 44     root[x] = pre;
 45     return root[x];
 46 }
 47 
 48 void init()
 49 {
 50     cnt = 0;
 51     memset(rela, 0, sizeof(rela));
 52 }
 53 
 54 void read()
 55 {
 56     scanf("%d", &N);
 57     init();
 58     
 59     int x, y; char str[5];
 60     for (int i = 1; i <= N; i++ )
 61     {
 62         scanf("%d %d %s", &lisan[i].lhs, &lisan[i].rhs, str );
 63         lisan[i].rela = str[0]=='e' ? 0 : 1;
 64         num[++cnt] = lisan[i].lhs-1; num[++cnt] = lisan[i].rhs;
 65     }
 66 
 67     sort(num+1, num+1+cnt);
 68     n = unique(num+1, num+1+cnt)-num-1;
 69 }
 70 
 71 int main()
 72 {
 73     while (~scanf("%d", &len))
 74     {
 75         read();
 76 
 77         for ( int i = 0; i <= n; i++ )
 78             root[i] = i;
 79 
 80         int x, y, xr, yr;
 81         for ( int i = 1; i <= N; i++ )
 82         {
 83             x = lower_bound(num+1, num+1+n, lisan[i].lhs-1)-num;
 84             y = lower_bound(num+1, num+1+n, lisan[i].rhs)-num;
 85 
 86             xr = _find(x); yr = _find(y);
 87             if ( xr == yr )
 88             {
 89                 if ( rela[x]^rela[y] != lisan[i].rela )
 90                     { printf("%d\n", i-1); return 0; }
 91             }
 92             else
 93             {
 94                 root[xr] = yr;
 95                 rela[xr] = rela[x]^rela[y]^lisan[i].rela;
 96             }
 97         }
 98         printf("%d", N);
 99     }
100 
101     return 0;
102 }
View Code

 

posted @ 2018-06-02 16:25  LBNOQYX  阅读(211)  评论(0编辑  收藏  举报