并查集专题

 普通的并查集:

pku 1308 Is It A Tree?

 

http://poj.org/problem?id=1308

 

判断点的关系是否满足是一棵树,这里考察细节,注意:1:空的也是树;2:注意可能形成森林,森林就不是所描述的树了

View Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <string>

#define CL(a,num) memset((a),(num),sizeof(a))
#define iabs(x)  ((x) > 0 ? (x) : -(x))
#define Min(a , b) ((a) < (b) ? (a) : (b))
#define Max(a , b) ((a) > (b) ? (a) : (b))

#define ll __int64
#define inf 0x7f7f7f7f
#define MOD 100000007
#define lc l,m,rt<<1
#define rc m + 1,r,rt<<1|1
#define pi acos(-1.0)
#define test puts("<------------------->")
#define maxn 100007
#define M 100007
#define N 100007
using namespace std;
//freopen("din.txt","r",stdin);

int f[N];
bool flag;
bool vt[N];
int cs[N];

void init(){
    for (int i = 0; i < N; ++i){
        f[i] = i;
        vt[i] = false;
    }
}
int find(int x){
    if (x != f[x])
    f[x] = find(f[x]);
    return f[x];
}
void Union(int x,int y){
    int tx = find(x);
    int ty = find(y);
    if (tx != ty) f[ty] = tx;
    else flag = true;
}
int main(){
   //freopen("din.txt","r",stdin);
    int x,y,i;
    int cas = 1;
    int len;
    while (~scanf("%d%d",&x,&y)){
        if (x == -1 && y == -1) break;
        if (x == 0 && y == 0){
            printf("Case %d is a tree.\n",cas++);
            continue;
        }
        len = 0;
        flag = false;
        init();
        if (!vt[x]){vt[x] =  true; cs[len++] = x;}
        if (!vt[y]){vt[y] =  true; cs[len++] = y;}
        Union(x,y);
        while (scanf("%d%d",&x,&y)){
            if (!x && !y) break;
            if (!vt[x]){vt[x] =  true; cs[len++] = x;}
            if (!vt[y]){vt[y] =  true; cs[len++] = y;}
            if (!flag) Union(x,y);
        }
        if (!flag){
            int rt = find(cs[0]);
            for (i = 1; i < len; ++i){
                if (rt != find(cs[i])){
                    flag = true;
                    break;
                }
            }
        }
        if (!flag) printf("Case %d is a tree.\n",cas++);
        else printf("Case %d is not a tree.\n",cas++);
    }
    return 0;
}

 

pku 2236 Wireless Network

http://poj.org/problem?id=2236

题意:

给出n个电脑的坐标,给出两种操作,O x表示将第x个电脑修好,  S x,y表示询问第x个电脑是否可以与第y个电脑通信。  通信的要求:两台电脑必须都已经修好,而且两台电脑的距离小于d。两台电脑之间的距离可以通过中间点来缩小。 比如A 与 B 可以通信   B 与 C 可以通信 ,则A与C的距离为0 。  

首先用isok标记每台电脑是否被修好,当我们修好电脑i时,在寻找与他距离小于d的已经好得电脑的集合并加入到该集合。这样就保证了,能够通信的电脑都放到了同一集合中。

最后查看是否属于同一集合即可.

View Code
#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
#include <cstring>
#include <algorithm>
#include <string>
#include <set>
#include <functional>
#include <numeric>
#include <sstream>
#include <stack>
#include <map>
#include <queue>

#define CL(arr, val)    memset(arr, val, sizeof(arr))

#define ll long long
#define inf 0x7f7f7f7f
#define lc l,m,rt<<1
#define rc m + 1,r,rt<<1|1
#define pi acos(-1.0)
#define ll long long
#define L(x)    (x) << 1
#define R(x)    (x) << 1 | 1
#define MID(l, r)   (l + r) >> 1
#define Min(x, y)   (x) < (y) ? (x) : (y)
#define Max(x, y)   (x) < (y) ? (y) : (x)
#define E(x)        (1 << (x))
#define iabs(x)     (x) < 0 ? -(x) : (x)
#define OUT(x)  printf("%I64d\n", x)
#define lowbit(x)   (x)&(-x)
#define Read()  freopen("din.txt", "r", stdin)
#define Write() freopen("dout.txt", "w", stdout);


#define N 1007
#define M 1007
using namespace std;

struct ipoint
{
    double x,y;
}pt[N];

int f[N];
bool isok[N];
int n;
double d;

double dis(int i,int j)
{
    double x = pt[i].x - pt[j].x;
    double y = pt[i].y - pt[j].y;
    double s = sqrt(x*x + y*y);
    return s;
}
int find(int x)
{
    if (f[x] != x) f[x] = find(f[x]);
    return f[x];
}
void Union(int x,int y)
{
    x = find(x); y = find(y);
    if (x != y) f[x] = y;
}
int main()
{
    int i;
    char op[3];
    int x,y;
    scanf("%d%lf",&n,&d);
    for (i = 0; i < n; ++i)
    {
        f[i] = i; isok[i] = false;
        scanf("%lf%lf",&pt[i].x,&pt[i].y);
    }
    f[n] = n; isok[n] = false;

    while (~scanf("%s%d",op,&x))
    {
        if (op[0] == 'O')
        {
            isok[x] = true;
            for (i = 1; i <= n; ++i)
            {
                if (dis(i - 1,x - 1) <= d && isok[i] && i != x)
                {
//                    printf("******%d %d\n",i,x);
                    Union(i,x);
                }
            }
        }
        else
        {

            scanf("%d",&y);
            int tx = find(x);
            int ty = find(y);
            if (tx != ty)
            {
                printf("FAIL\n");
            }
            else
            {
                printf("SUCCESS\n");
            }
        }
//        for (i = 1; i <= n; ++i)
//        {
//            printf(">%d %d\n",i,find(i));
//        }
    }
    return 0;
}

 

pku 2524 

 http://poj.org/problem?id=2524

裸的简单并查集:判断集合的个数

pku 1861

http://poj.org/problem?id=1861

 Kruscal应用,它不仅保证了得到的是最小生成树,而且还保证了,每条边肯定选择的是最小的。

 

关系并查集:

pku 1703 Find them, Catch them

http://poj.org/problem?id=1703

题意:

总共存在两个帮派,有两种操作D [a] [b] 说明a,b属于不同的帮派,A [a] [b] 需要我们来判断他们的关系,属于同一帮派输出,属于不同的帮派,或者不确定。

思路:

利用并查集构成的树的长度关系,我们把不同的利用并查集合并,然后维持一个他们到根节点的距离dep我们只要判断他们的距离的差值是否为2的倍数就可判断时代否在同以帮派了。

View Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <string>

#define CL(a,num) memset((a),(num),sizeof(a))
#define iabs(x)  ((x) > 0 ? (x) : -(x))
#define Min(a , b) ((a) < (b) ? (a) : (b))
#define Max(a , b) ((a) > (b) ? (a) : (b))

#define ll __int64
#define inf 0x7f7f7f7f
#define MOD 100000007
#define lc l,m,rt<<1
#define rc m + 1,r,rt<<1|1
#define pi acos(-1.0)
#define test puts("<------------------->")
#define maxn 100007
#define M 100007
#define N 100007
using namespace std;
//freopen("din.txt","r",stdin);

int dep[N],f[N];
int n;

//初始化
void init(){
    int i;
    for (i = 0; i <= n; ++i){
        f[i] = i;
        dep[i] = 0;
    }
}
//find函数里面维护dep
int find(int x){
    if (x != f[x]){
        int tmp = f[x];
        f[x] = find(f[x]);
        dep[x] = (dep[tmp] + dep[x])%2;//更新未压缩的点,保持dep的正确性
    }
    return f[x];
}
void Union(int x,int y){
    int tx = find(x);
    int ty = find(y);
    if (tx != ty){
        f[ty] = tx;
        dep[ty] = (dep[x] + dep[y] + 1)%2;//维护根节点的正确性
    }
}
int main(){
    //freopen("din.txt","r",stdin);
    int i;
    int T,m;
    char op[3];
    int x,y;
    scanf("%d",&T);
    while (T--){
        scanf("%d%d",&n,&m);
        init();
        for (i = 0; i < m; ++i){
            scanf("%s%d%d",op,&x,&y);
            if (op[0] == 'D'){
                Union(x,y);
            }
            else{
                int tx = find(x);
                int ty = find(y);
                if (tx != ty) printf("Not sure yet.\n");
                else{
                    if (iabs(dep[x] - dep[y])%2 == 0) printf("In the same gang.\n");
                    else printf("In different gangs.\n");
                }
            }
        }
    }
    return 0;
}

 

 

pku 1182 食物链

http://poj.org/problem?id=1182

题意:中文略

思路:

这个理还是根据长度来确定他们的关系:父是子的关系:0:同类,1:食物,2:天敌。

当他们不属于同一集合时,合并:

例如4,6是食物关系,则有0 + x = 1 + 2 我们可以退出x,y是食物关系时,有dep[fx] = dep[y] + 1 - dep[x];

若是同类关系则有 0 + x = 0 + 2 则可推出 dep[fx] = dep[y] + 0 - dep[x];

然后就是在find里面维护dep了。

View Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <string>

#define CL(a,num) memset((a),(num),sizeof(a))
#define iabs(x)  ((x) > 0 ? (x) : -(x))
#define Min(a , b) ((a) < (b) ? (a) : (b))
#define Max(a , b) ((a) > (b) ? (a) : (b))

#define ll __int64
#define inf 0x7f7f7f7f
#define MOD 100000007
#define lc l,m,rt<<1
#define rc m + 1,r,rt<<1|1
#define pi acos(-1.0)
#define test puts("<------------------->")
#define maxn 100007
#define M 100007
#define N 100007
using namespace std;
//freopen("din.txt","r",stdin);

int dep[N],f[N];
int n,k;
int ct;

void init(){
    int i;
    for (i = 0; i <= n; ++i){
        f[i] = i;
        dep[i] = 0;
    }
}
int find(int x){
    if (x != f[x]){
        int tmp = f[x];
        f[x] = find(f[x]);
        dep[x] = (dep[x] + dep[tmp])%3;
    }
    return f[x];
}
void Union(int x,int y,int mk){
    int tx = find(x);
    int ty = find(y);
    if (tx != ty){
        f[tx] = ty;
        dep[tx] = (dep[y] + mk - dep[x])%3;//维护dep数组
    }
    else{
        if ((dep[x] - dep[y] + 3)%3 != mk) ct++;
    }
}
int main(){
  //  freopen("din.txt","r",stdin);
    ct = 0;
    int op,x,y;
    scanf("%d%d",&n,&k);
    init();
    while (k--){
        scanf("%d%d%d",&op,&x,&y);
        if (x > n || y > n || (op == 2 && x == y)){
            ct++;
            continue;
        }
        if (op == 1){
            Union(x,y,0);//同类合并时,间距为0
        }
        else{
            Union(x,y,1);//不是关系时,间距为1
        }
    }
    printf("%d\n",ct);
    return 0;
}

 

pku 2492

http://poj.org/problem?id=2492

关系并查集的水题,要么属于1要么属于0.和1703一样的。。

View Code
#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
#include <cstring>
#include <algorithm>
#include <string>
#include <set>
#include <functional>
#include <numeric>
#include <sstream>
#include <stack>
#include <map>
#include <queue>

#define CL(arr, val)    memset(arr, val, sizeof(arr))

#define ll long long
#define inf 0x7f7f7f7f
#define lc l,m,rt<<1
#define rc m + 1,r,rt<<1|1
#define pi acos(-1.0)
#define ll long long
#define L(x)    (x) << 1
#define R(x)    (x) << 1 | 1
#define MID(l, r)   (l + r) >> 1
#define Min(x, y)   (x) < (y) ? (x) : (y)
#define Max(x, y)   (x) < (y) ? (y) : (x)
#define E(x)        (1 << (x))
#define iabs(x)     (x) < 0 ? -(x) : (x)
#define OUT(x)  printf("%I64d\n", x)
#define lowbit(x)   (x)&(-x)
#define Read()  freopen("din.txt", "r", stdin)
#define Write() freopen("dout.txt", "w", stdout);


#define N 2007
#define M 2007
using namespace std;

int f[N],dep[N];

int n,m;

int find(int x)
{
    int fa = 0;
    if (f[x] != x)
    {
        fa = f[x];
        f[x] = find(f[x]);
        dep[x] = (dep[x] + dep[fa])%2;
    }
    return f[x];
}
int main()
{
    int i;
    int x,y;
    int T,cas = 1;
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d%d",&n,&m);
        for (i = 1; i <= n; ++i) f[i] = i,dep[i] = 0;
        bool flag = false;
        while (m--)
        {
            scanf("%d%d",&x,&y);
            if (!flag)
            {
                int tx = find(x);
                int ty = find(y);
                if (tx != ty)
                {
                    f[tx] = ty;
                    dep[tx] = (dep[y] + 1 - dep[x])%2;
                }
                else
                {
                    if ((iabs(dep[x] - dep[y]))%2 == 0)
                    {
                        flag = true;
                    }
                }
            }
        }
        printf("Scenario #%d:\n",cas++);
        if (flag) printf("Suspicious bugs found!\n");
        else printf("No suspicious bugs found!\n");
        printf("\n");
    }
    return 0;
}

 

pku 1733  Parity game

http://poj.org/problem?id=1733

题意:

给定长度为n的序列,每个位置i要么是0要么是1,给出一些该序列的信息, x,y,res 表示在[x,y]这个区间里里面存在偶数个1还是奇数个1。让我们依据信息往下走判断信息的正确性,直到遇到错误信息跳出,然后输出在出错之前正确的信息个数。

思路:

这道题目并查集确实不好想,我的思路是记录每个信息x与y的关系,每次都根据距离根结点的长度来判断x与y的关系,可是当有[1,2] [3,4] [5,6]时[1,6]之间的关系是确定的,可是单纯记录x,y的关系的话,是不能确定他们之间的关系的。所以我们要向前一步记录[x-1,y]的关系,这样就保证了,最后他们的根节点都可归结于0,[1,2][3,4]确定关系后,[1,4]肯定能够确定关系了。

View Code
#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
#include <cstring>
#include <algorithm>
#include <string>
#include <set>
#include <functional>
#include <numeric>
#include <sstream>
#include <stack>
#include <map>
#include <queue>

#define CL(arr, val)    memset(arr, val, sizeof(arr))

#define ll long long
#define inf 0x7f7f7f7f
#define lc l,m,rt<<1
#define rc m + 1,r,rt<<1|1
#define pi acos(-1.0)
#define ll long long
#define L(x)    (x) << 1
#define R(x)    (x) << 1 | 1
#define MID(l, r)   (l + r) >> 1
#define Min(x, y)   (x) < (y) ? (x) : (y)
#define Max(x, y)   (x) < (y) ? (y) : (x)
#define E(x)        (1 << (x))
#define iabs(x)     (x) < 0 ? -(x) : (x)
#define OUT(x)  printf("%I64d\n", x)
#define lowbit(x)   (x)&(-x)
#define Read()  freopen("din.txt", "r", stdin)
#define Write() freopen("dout.txt", "w", stdout);


#define N 5007
#define M 15007
using namespace std;

struct node
{
    int x,y;
    char res[10];
}nd[N];

int a[2*N],len,f[2*N],dep[2*N];

int n;

void init(int m)
{
    for (int i = 0; i <= m; ++i)
    {
        f[i] = i;
        dep[i] = 0;
    }
}
int Search(int l,int r,int val)
{
    while (l <= r)
    {
        int mid = (l + r)/2;
        if (a[mid] == val) return mid;
        else if (a[mid] > val) r = mid - 1;
        else l = mid + 1;
    }
    return -1;
}
int find(int x)
{
    int fa = 0;
    if (f[x] != x)
    {
        fa = f[x];
        f[x] = find(f[x]);
        dep[x] = (dep[x] + dep[fa])%2;
    }
    return f[x];
}
int cmp(int a,int b)
{
    return a < b;
}
int main()
{
//    Read();
    int i;
    scanf("%d",&n);
    scanf("%d",&n);
    len = 1;
    for (i = 0; i < n; ++i)
    {
        scanf("%d%d%s",&nd[i].x,&nd[i].y,nd[i].res);
        a[len++] = nd[i].x; a[len++] = nd[i].y;
    }


    sort(a + 1,a + len,cmp);//这里的长度是len-1


    int tn = 1;
    for (i = 2; i < len; ++i) if (a[i] != a[i - 1]) a[++tn] = a[i];


    init(tn);
    int ans = 0;
    for (i = 0; i < n; ++i)
    {
        int x = Search(1,tn,nd[i].x);
        int y = Search(1,tn,nd[i].y);

        x--;
        int tx = find(x);
        int ty = find(y);
        int add = 0;
        if (nd[i].res[0] == 'e') add = 0;
        else add = 1;
        if (tx != ty)
        {
            f[ty] = tx;
            dep[ty] = (dep[x] + add - dep[y])%2;
        }
        else
        {
            if ((iabs(dep[y] - dep[x]))%2 != add) break;
        }
        ans++;
    }
    printf("%d\n",ans);

    return 0;
}

 

 HDU 3038 How Many Answers Are Wrong

http://acm.hdu.edu.cn/showproblem.php?pid=3038

同上题类似的题目,只不过改成了求和。。

 

View Code
#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
#include <cstring>
#include <algorithm>
#include <string>
#include <set>
#include <functional>
#include <numeric>
#include <sstream>
#include <stack>
#include <map>
#include <queue>

#define CL(arr, val)    memset(arr, val, sizeof(arr))

#define ll __int64
#define inf 0x7f7f7f7f
#define lc l,m,rt<<1
#define rc m + 1,r,rt<<1|1
#define pi acos(-1.0)

#define L(x)    (x) << 1
#define R(x)    (x) << 1 | 1
#define MID(l, r)   (l + r) >> 1
#define Min(x, y)   (x) < (y) ? (x) : (y)
#define Max(x, y)   (x) < (y) ? (y) : (x)
#define E(x)        (1 << (x))
#define iabs(x)     (x) < 0 ? -(x) : (x)
#define OUT(x)  printf("%I64d\n", x)
#define lowbit(x)   (x)&(-x)
#define Read()  freopen("din.txt", "r", stdin)
#define Write() freopen("dout.txt", "w", stdout);


#define N 200007
#define M 15007
using namespace std;

int n,m;
int f[N];
ll dep[N];

void init(int len)
{
    for (int i = 0; i <= len; ++i)
    {
        f[i] = i;
        dep[i] = 0;
    }
}

int find(int x)
{
    int fa = 0;
    if (f[x] != x)
    {
        fa = f[x];
        f[x] = find(f[x]);
        dep[x] = (dep[x] + dep[fa]);
    }
    return f[x];
}
int main()
{
//    Read();
    int x,y,w;

    while (~scanf("%d%d",&n,&m))
    {
        init(n);
        int ans = 0;
        while (m--)
        {
            scanf("%d%d%d",&x,&y,&w);
            x--;
            int tx = find(x);
            int ty = find(y);

            if (tx != ty)
            {
                f[ty] = tx;
                dep[ty] = (dep[x] + w - dep[y]);
            }
            else
            {
                if (iabs(dep[y] - dep[x]) != w) ans++;
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

 pku 1417 True Liars

http://poj.org/problem?id=1417

题意:

一伙人中有p1个诚实的人,p2个说谎的人,诚实的人总是说实话,说谎的人总是说谎话。给出n个询问,x,y,string,表示问x,y是不是诚实的人,string(yes,no)。求是否能够唯一确定这p1个诚实的人,如果能输出者n个人,否则输出no.

思路:

由于诚实的人总是说真话,说谎的人总是说谎话。这里的人要么是诚实的,要么是是说谎的,当出现yes时,肯定x,y属于同一类人,出现no时,x,y属于不同类的人。根据这个利用并查集将p1+p2个人进行合并分组,每一组都会分成两种,一种是诚实人的个数,一种是说谎人的个数。然后利用DP求到达dp[len][p1]时,是否只有一种方法,即可。len表示分分出的组数,dp[i][j]表示到达第i个分组时有j个诚实的人的方法数。

ps:这里遇到个问题,不知道为什么,并查集处理的时候我的的 dep[tx] = (dep[y] + add - dep[x])%2; dep[x] = (dep[x] + dep[fa])%2;处理WA, 改成d ep[tx] = dep[y]^dep[x]^add; dep[x] = dep[x]^dep[fa];就对了呢?不过我觉得是一样的思想啊,不知为何。

View Code
#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
#include <cstring>
#include <algorithm>
#include <string>
#include <set>
#include <functional>
#include <numeric>
#include <sstream>
#include <stack>
#include <map>
#include <queue>

#define CL(arr, val)    memset(arr, val, sizeof(arr))

#define ll __int64
#define inf 0x7f7f7f7f
#define lc l,m,rt<<1
#define rc m + 1,r,rt<<1|1
#define pi acos(-1.0)

#define L(x)    (x) << 1
#define R(x)    (x) << 1 | 1
#define MID(l, r)   (l + r) >> 1
#define Min(x, y)   (x) < (y) ? (x) : (y)
#define Max(x, y)   (x) < (y) ? (y) : (x)
#define E(x)        (1 << (x))
#define iabs(x)     (x) < 0 ? -(x) : (x)
#define OUT(x)  printf("%I64d\n", x)
#define lowbit(x)   (x)&(-x)
#define Read()  freopen("din.txt", "r", stdin)
#define Write() freopen("dout.txt", "w", stdout);


#define N 602
#define M 15007
using namespace std;

int a[N][2];
int f[N],dep[N];
int len,n,p1,p2;

vector<int>ivc[N][2],ans;
int root[N];

int dp[N][N],pre[N][N];

void init(int m)
{
    for (int i = 0; i <= m; ++i)
    {
        f[i] = i;
        dep[i] = 0;
        ivc[i][0].clear();
        ivc[i][1].clear();
    }
}
int find(int x)
{
    int fa = 0;
    if (f[x] != x)
    {
        fa = f[x];
        f[x] = find(f[x]);
        dep[x] = dep[x]^dep[fa];
    }
    return f[x];
}
void Union(int x,int y,int add)
{
    int tx = find(x);
    int ty = find(y);
    if (tx != ty)
    {
        f[tx] = ty;
        dep[tx] = dep[y]^dep[x]^add;
    }
}
int main()
{
//    Read();
    int i,j;
    int x,y;
    char res[5];
    while (~scanf("%d%d%d",&n,&p1,&p2))
    {
        if (!n && !p1 && !p2) break;
        init(p1 + p2);
        for (i = 0; i < n; ++i)
        {
            scanf("%d%d%s",&x,&y,res);
            if (res[0] == 'n') Union(x,y,1);
            else Union(x,y,0);
        }

        len = 0; CL(a,0);

        for (i = 1; i <= p1 + p2; ++i)
        {
            if (find(i) == i)
            {
                root[++len] = i;
                for (j = 1; j <= p1 + p2; ++j)
                {
                    if (find(j) == i)
                    {
//                        printf("*****%d\n",dep[j]);
                        a[len][dep[j]]++;
                        ivc[len][dep[j]].push_back(j);
                    }
                }
            }
        }

        CL(dp,0); CL(pre,0);

        dp[0][0] = 1;
        for (i = 1; i <= len; ++i)
        {
            for (j = 0; j <= p1; ++j)
            {
                if (j >= a[i][0])
                {
                    dp[i][j] += dp[i - 1][j - a[i][0]];
                    if (dp[i - 1][j - a[i][0]] == 1) pre[i][j] = 0;
                }
                if (j >= a[i][1])
                {
                    dp[i][j] += dp[i - 1][j - a[i][1]];
                    if (dp[i - 1][j - a[i][1]] == 1) pre[i][j] = 1;
                }
            }
        }
//        printf(">>>>>%d %d %d\n",len,dp[len][p1],pre[len][p1]);
        if (dp[len][p1] != 1) printf("no\n");
        else
        {
            ans.clear();
            for (i = len; i >= 1; --i)
            {
                if (pre[i][p1] == 0)
                {
//                    printf("0*********\n");
                    for (j = 0; j < ivc[i][0].size(); ++j) ans.push_back(ivc[i][0][j]);
                    p1 -= a[i][0];
                }
                else if (pre[i][p1] == 1)
                {
//                    printf("1*********\n");
                    for (j = 0; j < ivc[i][1].size(); ++j) ans.push_back(ivc[i][1][j]);
                    p1 -= a[i][1];
                }
            }
            sort(ans.begin(),ans.end());
            vector<int>::iterator it;
            for (it = ans.begin(); it != ans.end(); ++it)
            printf("%d\n",*it);
            printf("end\n");
        }
    }
    return 0;
}

 

pku 2912 Rochambeau

http://poj.org/problem?id=2912

题意:

n个人分成三组,玩石头剪子布游戏,同一组的人只能出同样固定的的手势,其中有一个是裁判不属于任何组,可以出任意手势,给出m个信息x op y 表示x,y是从三个组里面随机抽取的或者是裁判,他们之间的输赢关系。让你判断最少在第几组信息时,可以唯一判断出裁判,并将裁判号,以及在第几组后判断出来的输出。

思路:
注意这里是能够唯一确定,也就是存在确定的唯一一个裁判。那么我们可以从n个人里面枚举裁判,然后判断除去裁判之后是否还存在矛盾的关系(存在矛盾肯定是裁判在其中导致的),如果存在那么排除该人,记录在第几个信息出现的。枚举完后,如果只存在一个矛盾的人那么这个人就是裁判,如果不存在矛盾,输出Impossible,否则就表示存在多个裁判,输出Can not determine 这里存在矛盾的判断就是用分类并查集处理的了。

View Code
#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
#include <cstring>
#include <algorithm>
#include <string>
#include <set>
#include <functional>
#include <numeric>
#include <sstream>
#include <stack>
#include <map>
#include <queue>

#define CL(arr, val)    memset(arr, val, sizeof(arr))

#define ll __int64
#define inf 0x7f7f7f7f
#define lc l,m,rt<<1
#define rc m + 1,r,rt<<1|1
#define pi acos(-1.0)

#define L(x)    (x) << 1
#define R(x)    (x) << 1 | 1
#define MID(l, r)   (l + r) >> 1
#define Min(x, y)   (x) < (y) ? (x) : (y)
#define Max(x, y)   (x) < (y) ? (y) : (x)
#define E(x)        (1 << (x))
#define iabs(x)     (x) < 0 ? -(x) : (x)
#define OUT(x)  printf("%I64d\n", x)
#define lowbit(x)   (x)&(-x)
#define Read()  freopen("din.txt", "r", stdin)
#define Write() freopen("dout.txt", "w", stdout);


#define N 502
#define M 2007
using namespace std;

struct node
{
    int x,y;
    char op;
}nd[M];

int n,m;
int f[N],dep[N];
int err[N];

void init(int len)
{
    for (int i = 0; i <= len; ++i)
    {
        f[i] = i;
        dep[i] = 0;
    }
}
int find(int x)
{
    int fa = 0;
    if (f[x] != x)
    {
        fa = f[x];
        f[x] = find(f[x]);
        dep[x] = (dep[x] + dep[fa])%3;
    }
    return f[x];
}
int main()
{
//    Read();
    int i,j;
    while (~scanf("%d%d",&n,&m))
    {

        for (i = 0; i < m; ++i) scanf("%d%c%d",&nd[i].x,&nd[i].op,&nd[i].y);

        CL(err,0);//记录当i为裁判时出现矛盾在第几个信息处
        for (i = 0; i < n; ++i)
        {
            init(n);
            for (j = 0; j < m; ++j)
            {
                if (nd[j].x == i || nd[j].y == i) continue;
                int tx = find(nd[j].x);
                int ty = find(nd[j].y);

                if (tx == ty)
                {
                    if (nd[j].op == '=' && (dep[nd[j].x] - dep[nd[j].y])%3 != 0)
                    {
                        err[i] = j + 1;
                        break;
                    }
                    else if (nd[j].op == '<' && (dep[nd[j].x] - dep[nd[j].y])%3 != 1 && (dep[nd[j].y] - dep[nd[j].x])%3 != 2)
                    {
                        err[i] = j + 1;
                        break;
                    }
                    else if (nd[j].op == '>' && (dep[nd[j].y] - dep[nd[j].x])%3 != 1 && (dep[nd[j].x] - dep[nd[j].y])%3 != 2)
                    {
                        err[i] = j + 1;
                        break;
                    }
                }
                else
                {
                    if (nd[j].op == '=')
                    {
                        f[tx] = ty;
                        dep[tx] = (dep[nd[j].y] + 0 - dep[nd[j].x])%3;
                    }
                    else if (nd[j].op == '<')
                    {
                        f[tx] = ty;
                        dep[tx] = (dep[nd[j].y] + 1 - dep[nd[j].x])%3;
                    }
                    else
                    {
                        f[ty] = tx;
                        dep[ty] = (dep[nd[j].x] + 1 - dep[nd[j].y])%3;
                    }
                }
            }
        }

        int k = 0, ans = 0;
        int ct = 0;
        for (i = 0; i < n; ++i)
        {
            if (err[i] == 0)
            {
                k = i;
                ct++;
            }
            if (err[i] > ans) ans = err[i];
        }
        if (ct == 0) printf("Impossible\n");
        else if (ct == 1) printf("Player %d can be determined to be the judge after %d lines\n",k,ans);
        else printf("Can not determine\n");
    }
    return 0;
}

 

 

 

扩展的并查集

pku 1611 The Suspects

http://poj.org/problem?id=1611

记录集合数目:

View Code
#include <iostream>
#include <cstdio>
#include <cstring>
#define maxn 30007
using namespace std;

int f[maxn],num[maxn];
int n,m,k;

void init()
{
    for (int i = 0; i < maxn; ++i)
    {
        f[i] = i; num[i] = 1;
    }
}

int find(int x)
{
    if (x != f[x])
    f[x] = find(f[x]);
    return f[x];
}

void Union(int x,int y)
{
    int tx = find(x);
    int ty = find(y);
    if (tx != ty)
    {
        f[tx] = ty;
        num[ty] += num[tx];
    }
}
int main()
{
    int i,j,a,b;
    while (~scanf("%d%d",&n,&m))
    {
        if (!n && !m) break;
        init();
        for (i = 0; i < m; ++i)
        {
            scanf("%d",&k);
            if (k) scanf("%d",&a);
            for (j = 1; j < k; ++j)
            {
                scanf("%d",&b);
                Union(a,b);
            }
        }
        int pos = find(0);
        printf("%d\n",num[pos]);
    }
    return 0;
}

 pku 1988

http://poj.org/problem?id=1988

题意:

有n个栈,每个栈里面放着一个有标号的立方体。对其进行如下操作:

M:x,y 表示把含有标号x的栈放到含有标号y的栈里面;

C:x 表示计算在标号x下边的立方体的个数;

思路:

并查集表示累在一起的立方体的集合,记录每个根节点所有子节点的个数all[i]表示i节点下边一共有多少个立方体。然后用up记录每一个节点的上边节点的个数,最后求的是:

all[root[i]] - up[i]- 1 

#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
#include <cstring>
#include <algorithm>
#include <string>
#include <set>
#include <functional>
#include <numeric>
#include <sstream>
#include <stack>
#include <map>
#include <queue>

#define CL(arr, val)    memset(arr, val, sizeof(arr))

#define lc l,m,rt<<1
#define rc m + 1,r,rt<<1|1
#define pi acos(-1.0)
#define ll long long
#define L(x)    (x) << 1
#define R(x)    (x) << 1 | 1
#define MID(l, r)   (l + r) >> 1
#define Min(x, y)   (x) < (y) ? (x) : (y)
#define Max(x, y)   (x) < (y) ? (y) : (x)
#define E(x)        (1 << (x))
#define iabs(x)     (x) < 0 ? -(x) : (x)
#define OUT(x)  printf("%I64d\n", x)
#define lowbit(x)   (x)&(-x)
#define Read()  freopen("din.txt", "r", stdin)
#define Write() freopen("dout.txt", "w", stdout);


#define M 500007
#define N 30007
using namespace std;

int f[N],up[N],all[N];
int n;

void init()
{
    int i;
    for (i = 0; i < N; ++i)
    {
        f[i] = i;
        up[i] = 0;
        all[i] = 1;
    }
}
int find(int x)
{
    int fa = 0;
    if (f[x] != x)
    {
        fa = f[x];
        f[x] = find(f[x]);
        up[x] += up[fa];//类似于分类并查集的递归更新
    }
    return f[x];
}
void Union(int x,int y)
{
    int tx = find(x);
    int ty = find(y);
    if (tx != ty)
    {
        f[ty] = tx;
        up[ty] = all[tx];
        all[tx] += all[ty];
    }
}
int main()
{
    int i,x,y;
    char op[2];
    while (~scanf("%d",&n))
    {
        init();
        for (i = 0; i < n; ++i)
        {
            scanf("%s%d",op,&x);
            if (op[0] == 'M')
            {
                scanf("%d",&y);
                Union(x,y);
            }
            else
            {
                int root = find(x);
                printf("%d\n",all[root] - up[x] - 1);
            }
        }
    }
    return 0;
}

  

 pku http://poj.org/problem?id=1456

详解见:http://www.cnblogs.com/E-star/archive/2013/03/04/2943268.html

 zoj 3261 Connections in Galaxy War

 http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=3563

 题意: 

给定n个银河里的行星,以及每个行星的能量值,给出m个边x,y表示行星x与y可以互达。 给出Q个操作,有两种操作destroy a,b,表示把a与b的边毁掉,query a 表示a行星要向其他与他相连的可达的行星求救,如果可以输出该行星的编号,否则输出-1 . 可求救的行星需要满足他的能量值比a大,并且可以a互通。 如果存在最大值一样的输出编号最小的。

思路:
逆序合并,首先离线保存所有输入,然后把所有不被毁坏的边用并查集合并点,倒着处理,当遇到destroy是然后再合并a,b 。并查集的根节点存放该集合里val值得最大的,然后只要该集合的点询问时询问根节点就行。

ps:注意:当遇到query时,我们可能会遇到询问某个集合的节点a(非根节点)是得到的根节点b 他们之间的val值相同,这样的情况是不能寻求帮助的输出-1;

View Code
#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
#include <cstring>
#include <algorithm>
#include <string>
#include <set>
#include <functional>
#include <numeric>
#include <sstream>
#include <stack>
#include <map>
#include <queue>

#define CL(arr, val)    memset(arr, val, sizeof(arr))

#define ll long long
#define inf 0x7f7f7f7f
#define lc l,m,rt<<1
#define rc m + 1,r,rt<<1|1
#define pi acos(-1.0)

#define L(x)    (x) << 1
#define R(x)    (x) << 1 | 1
#define MID(l, r)   (l + r) >> 1
#define Min(x, y)   (x) < (y) ? (x) : (y)
#define Max(x, y)   (x) < (y) ? (y) : (x)
#define E(x)        (1 << (x))
#define iabs(x)     (x) < 0 ? -(x) : (x)
#define OUT(x)  printf("%I64d\n", x)
#define lowbit(x)   (x)&(-x)
#define Read()  freopen("din.txt", "r", stdin)
#define Write() freopen("dout.txt", "w", stdout);


#define N 10007
#define M 20007
#define Q 50007
using namespace std;

int f[N];
int val[N];

vector<int>ivc[N];
int a[Q],b[Q];
int ans[M];
int n,m,q;


void init(int len)
{
    for (int i = 0; i <= len; ++i) f[i] = i;
}
int find(int x)
{
    if (f[x] != x) f[x] = find(f[x]);
    return f[x];
}
void Union(int x,int y)
{
    int tx = find(x);
    int ty = find(y);

    if (tx != ty)
    {
        if (val[tx] < val[ty]) f[tx] = ty;
        else if (val[tx] > val[ty]) f[ty] = tx;
        else
        {
            if (tx < ty) f[ty] = tx;
            else f[tx] = ty;
        }
    }
}
int main()
{
//    Read();
    int i;
    int x,y;
    vector<int>::iterator it;
    int cas = 0;
    while (~scanf("%d",&n))
    {
        if (cas == 1) printf("\n");
        for (i = 0; i < n; ++i) scanf("%d",&val[i]);

        scanf("%d",&m);

        for (i = 0; i < n; ++i) ivc[i].clear();

        for (i = 0; i < m; ++i)
        {
            scanf("%d%d",&x,&y);
            if (x > y) swap(x,y);
            ivc[x].push_back(y);
        }

        char op[10];
        scanf("%d",&q);
        for (i = 0; i < q; ++i)
        {
            scanf("%s",op);
            if (op[0] == 'q')
            {
                scanf("%d",&a[i]);
                b[i] = -1;
            }
            else
            {
                scanf("%d%d",&a[i],&b[i]);
                if (a[i] > b[i]) swap(a[i],b[i]);
                for (it = ivc[a[i]].begin(); it != ivc[a[i]].end(); ++it)
                {
                    if (*it == b[i])//把要删除的点处理掉
                    {
                        *it = -1;
                    }
                }
            }
        }
        init(n);
        for (i = 0; i < n; ++i)
        {
            for (it = ivc[i].begin(); it != ivc[i].end(); ++it)
            {
                if (*it != -1) Union(i,*it);
            }
        }

        int len = 0;
        for (i = q - 1; i >= 0; --i)
        {
            if (b[i] == -1)
            {
                int fa = find(a[i]);
                if (val[fa] != val[a[i]])//注意这里一定要判断似val不能判断fa != a[i]
                {
                    ans[len++] = fa;
                }
                else
                {
                    ans[len++] = -1;
                }
            }
            else
            {
                Union(a[i],b[i]);
            }
        }
        for (i = len - 1; i >= 0; --i) printf("%d\n",ans[i]);
        cas = 1;
    }
    return 0;
}

 

 

posted @ 2012-10-05 19:32  E_star  阅读(1141)  评论(0编辑  收藏  举报