Find them, Catch them ,食物链-------并查集应用 偏移向量

1.Find them, Catch them

The police office in Tadu City decides to say ends to the chaos, as launch actions to root up the TWO gangs in the city, Gang Dragon and Gang Snake. However, the police first needs to identify which gang a criminal belongs to. The present question is, given two criminals; do they belong to a same clan? You must give your judgment based on incomplete information. (Since the gangsters are always acting secretly.)

Assume N (N <= 10^5) criminals are currently in Tadu City, numbered from 1 to N. And of course, at least one of them belongs to Gang Dragon, and the same for Gang Snake. You will be given M (M <= 10^5) messages in sequence, which are in the following two kinds:

1. D [a] [b]
where [a] and [b] are the numbers of two criminals, and they belong to different gangs.

2. A [a] [b]
where [a] and [b] are the numbers of two criminals. This requires you to decide whether a and b belong to a same gang.

Input

The first line of the input contains a single integer T (1 <= T <= 20), the number of test cases. Then T cases follow. Each test case begins with a line with two integers N and M, followed by M lines each containing one message as described above.

Output

For each message "A [a] [b]" in each case, your program should give the judgment based on the information got before. The answers might be one of "In the same gang.", "In different gangs." and "Not sure yet."

Sample Input

1
5 5
A 1 2
D 1 2
A 1 2
D 2 4
A 1 4

参考博客:

https://blog.csdn.net/IAccepted/article/details/21233745

题意:

就是有两个帮派,现在就是判断是不是属于同一个帮派

T 代表几组测试数据;

N代表有多少个帮派; 

M代表接下来几行询问

-----

A 1 2 //询问1和2是否属于同一个帮派-----输出Not sure yet.
D 1 2 //表示1和2不属于同一个帮派
A 1 2 //询问1和2是否属于同一个帮派-----输出In different gangs.  
D 2 4 //表示2和4不属于同一个帮派
A 1 4 //询问1和4是否属于同一个帮派-----输出In the same gangs.

上面的D是累加的,也就是每次的出现都是保留下来的,没有覆盖更新。

 

并查集的集合是以能否确定关系来分的,而不是以属于同一个帮派或不同帮派来分的
这类题只需建立一个并查集,然后维护每个结点和他的根结点之间的关系属性即可
如果2个点不在同一个并查集中,那么这2个点无法确定关系
在同一个并查集中的每个点,都能确定集合中的相互关系
定义一个数组c表示两个派别,0和1,初始化为0。

 

第一步:

首先确定用0、1分别标记某一个节点和树根的同、异帮派关系

第二步:

可以先认为每个节点都是树根且都是与自身同帮派,据此初始化标记变量

第三步:

对每组输入数据进行处理,若两个节点的根结点不同,若输入A操作,则无法确定二者的帮派是否相同;

若为D操作,则合并两个节点所在的树且更新节点的关系;

若两个节点的根结点相同,则根据这两个节点的与根结点的关系来判断这两个节点的关系。

 

#include<stdio.h>
const int maxn = 5000000;
int p[maxn]; //存父节点
int r[maxn];//存与父节点的关系
void set(int n) //初始化
{
    for(int x = 1; x <= n; x++)
    {
        p[x] = x;
        r[x] = 0;
    }
}
int find(int x) //找父亲节点
{
    if(x == p[x]) return x;

    int t = p[x];
    p[x] = find(p[x]);

    r[x] = (r[x]+r[t])%2;
    return p[x];
}
void Union(int x, int y)
{
    int fx = find(x);
    int fy = find(y);

    p[fy] = fx;
    r[fy] = (r[x]+r[y]+1)%2;
}

int main()
{
    int a,n, m;
    scanf("%d",&a);
    while(a--)
    {
        scanf("%d%d", &n, &m);

        set(n);

        char c;
        int x, y;
        while(m--)
        {
            getchar();
            scanf("%c%d%d", &c, &x, &y);
            if(c=='A')
            {
                if(find(x) == find(y))
                {
                    if(r[x]!= r[y]) puts("In different gangs.");
                    else puts("In the same gang.");
                }
                else puts("Not sure yet.");
            }
            else
                Union(x,y);
        }
    }
    return 0;
}

 

2.食物链

食物链
Time Limit: 1000MS           Memory Limit: 10000K
Total Submissions: 33805           Accepted: 9820

Description
动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A吃B, B吃C,C吃A。
现有N个动物,以1-N编号。每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。
有人用两种说法对这N个动物所构成的食物链关系进行描述:
第一种说法是"1 X Y",表示X和Y是同类。
第二种说法是"2 X Y",表示X吃Y。
此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
1) 当前的话与前面的某些真的话冲突,就是假话;
2) 当前的话中X或Y比N大,就是假话;
3) 当前的话表示X吃X,就是假话。
你的任务是根据给定的N(1 <= N <= 50,000)和K句话(0 <= K <= 100,000),输出假话的总数。

Input
第一行是两个整数N和K,以一个空格分隔。
以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。
若D=1,则表示X和Y是同类。
若D=2,则表示X吃Y。

Output
只有一个整数,表示假话的数目。

Sample Input

100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5

Sample Output

3

与上题类似,自己想不出来啊.....
参考博客:

https://blog.csdn.net/freezhanacmore/article/details/8767413

思路:把确定了相对关系的节点放在同一棵树中

            每个节点对应的 r[]值记录他与根节点的关系:

             0:同类,

             1:被父亲节点吃,

             2: 吃父亲节点

每次输入一组数据 d, x, y判断是否超过 N 后,先通过find()函数找他们的根节点从而判断他们是否在同一棵树中。(也就是是否有确定的关系)

1.如果在同一棵树中find(x) == find(y):直接判断是否说谎。

1)如果 d ==1,那么 x 与 y 应该是同类,他们的r[]应该相等

     如果不相等,则说谎数 +1

2)如果 d==2,那么 x 应该吃了 y,也就是 (r[x]+1)%3 == r[y]

     如果不满足,则说谎数 +1

    如何判断 x 吃了 y 是  (r[x]+1)%3 == r[y],请看下图:(PS:箭头方向指向被吃方)

      

2.如果不在同一棵树中:那么合并 x 与 y 分别所在的树。

   合并树时要注意顺序,我这里是把 x 树的根当做主根,否则会WA的很惨

注意:找父亲节点时,要不断更新 r[]的值

这里有一个关系:如果 x 和y 为关系 r1, y 和 z 为关系 r2

那么 x 和z的关系就是 (r1+r2)%

如何证明?

无非是3*3 = 9种情况而已

(a, b) 0:同类 、  1:a被b吃 、 2:a吃b


关于合并时r[]值的更新

如果 d == 1则 x和y 是同类 ,那么 y 对 x 的关系是 0

如果 d == 2 则 x 吃了 y,  那么 y 对 x 的关系是 1, x 对 y 的关系是 2.

综上所述 ,无论 d为1 或者是为 2,  y 对 x 的关系都是 d-1

定义 :fx 为 x 的根点, fy 为 y 的根节点

           合并时,如果把 y 树合并到 x 树中

           如何求 fy 对 fx 的r[]关系?

           fy 对 y 的关系为 3-r[y]

           y  对 x 的关系为 d-1

           x  对 fx 的关系为 r[x]

           所以 fy 对 fx 的关系是(3-r[y] + d-1 + r[x])%3

#include<stdio.h>
const int maxn = 50000+10;

int p[maxn]; //存父节点
int r[maxn];//存与父节点的关系 0 同一类,1被父节点吃,2吃父节点

void set(int n) //初始化
{
    for(int x = 1; x <= n; x++)
    {
        p[x] = x; //开始自己是自己的父亲节点
        r[x] = 0;//开始自己就是自己的父亲,每一个点均独立
    }
}

int find(int x) //找父亲节点
{
    if(x == p[x]) return x;

    int t = p[x];
    p[x] = find(p[x]);

    r[x] = (r[x]+r[t])%3; //回溯由子节点与父节点的关系和父节点与根节点的关系找子节点与根节点的关系
    return p[x];
}

void Union(int x, int y, int d)
{
    int fx = find(x);
    int fy = find(y);

    p[fy] = fx; //合并树 注意:被 x 吃,所以以 x 的根为父
    r[fy] = (r[x]-r[y]+3+(d-1))%3; //对应更新与父节点的关系
}

int main()
{
    int n, m;
    scanf("%d%d", &n, &m);
    set(n);

    int ans = 0;
    int d, x, y;
    while(m--)
    {
        scanf("%d%d%d", &d, &x, &y);

        if(x > n || y > n || (d == 2 && x == y)) ans++; //如果节点编号大于最大编号,或者自己吃自己,说谎



        else if(find(x) == find(y)) //如果原来有关系,也就是在同一棵树中,那么直接判断是否说谎
        {
            if(d == 1 && r[x] != r[y]) ans++; //如果 x 和 y 不属于同一类
            if(d == 2 && (r[x]+1)%3 != r[y]) ans++; // 如果 x 没有吃 y (注意要对应Uinon(x, y)的情况,否则一路WA到死啊!!!)
        }
        else Union(x, y, d); //如果开始没有关系,则建立关系
    }
    printf("%d\n", ans);
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值