Loading

kuangbin带你飞 专题五 并查集

kuangbin带你飞 专题五 并查集

POJ 2236 Wireless Network

大意:

给出n个电脑的坐标,一开始都是坏的,现在有两个操作,一个是修理坏的电脑,一个是测试修好的电脑的连通性

距离是d以内的电脑可以直接通信,距离大于d的也可以通过可以直接通信的电脑间接通信

要求输出每次测试的结果(能否通信)

思路:

维护一个已经修好的电脑的数组,每次修好一个新电脑都加到这个数组里,并且遍历这个数组,判断能否联通,能联通的话就合并并查集

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <vector>
#include <stack>
#include <queue>
#include <algorithm>
#include <math.h>
#include <cstdio>
using namespace std;

const int N = 1e6 + 5;
typedef long long LL;
int n;
double d;
struct node {
    double x, y;
} a[N];

int f[N];
int findf(int x) { return x == f[x] ? x : findf(f[x]); }
void Union(int x, int y) {
    int fx = findf(x);
    int fy = findf(y);
    if (fx != fy) {
        f[fy] = fx;
    }
}
int nice[N], num = 0;
int main() {
    cin >> n >> d;
    for (int i = 1; i <= n; i++) {
        cin >> a[i].x >> a[i].y;
        f[i] = i;
    }
    string op;
    while (cin >> op) {
        if (op == "S") {
            int x, y;
            cin >> x >> y;
            if (findf(x) == findf(y))
                cout << "SUCCESS" << endl;
            else
                cout << "FAIL" << endl;
        } else {
            int x;
            cin >> x;
            nice[num++] = x;
            for (int j = 0; j < num; j++) {
                if ((a[x].x - a[nice[j]].x) * (a[x].x - a[nice[j]].x) +
                        (a[x].y - a[nice[j]].y) * (a[x].y - a[nice[j]].y) <=
                    d * d)
                    Union(x, nice[j]);
            }
        }
    }
    return 0;
}

POJ 1611 The Suspects

大意:

n个学生,标号为0到n-1

m个社团,同一个社团的学生中如果有需要被隔离的学生,那么这个社团中所有的人都需要被隔离

0号学生是需要被隔离的,问一共需要隔离多少人

思路:

直接并查集模拟就可以

#include <math.h>
#include <stdio.h>
#include <string.h>

#include <algorithm>
#include <cstdio>
#include <iostream>
#include <queue>
#include <stack>
#include <vector>
using namespace std;

const int N = 1e6 + 5;
typedef long long LL;

int f[N];
int findf(int x) { return x == f[x] ? x : findf(f[x]); }
void Union(int x, int y) {
    int fx = findf(x);
    int fy = findf(y);
    if (fx != fy) {
        f[fy] = fx;
    }
}

int n, m;
int is[N], num = 0, tmp[N];
int main() {
    while (cin >> n >> m) {
        if (n + m == 0) break;
        for (int i = 0; i < n; i++) f[i] = i;
        for (int i = 0; i < m; i++) {
            int k;
            cin >> k;
            for (int i = 0; i < k; i++) {
                cin >> tmp[i];
                if (i) Union(tmp[i - 1], tmp[i]);
            }
        }
        int res = 0;
        for (int i = 0; i < n;i++){
            if (findf(0) == findf(i)) res++;
        }
        cout << res << endl;
    }
    return 0;
}

HDU 1213 How Many Tables

大意:

n个人,m对朋友,有朋友关系(直接或间接)的可以坐一桌,问需要多少个桌子

思路:

简单并查集

#include <math.h>
#include <stdio.h>
#include <string.h>
#include <set>
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <queue>
#include <stack>
#include <vector>
using namespace std;

const int N = 1e6 + 5;
typedef long long LL;

int f[N];
int findf(int x) { return x == f[x] ? x : findf(f[x]); }
void Union(int x, int y) {
    int fx = findf(x);
    int fy = findf(y);
    if (fx != fy) {
        f[fy] = fx;
    }
}

int n, m;
int main() {
    int t;
    cin >> t;
    while (t--) {
        cin >> n >> m;
        for (int i = 1; i <= n; i++) f[i] = i;
        for (int i = 0; i < m; i++) {
            int x, y;
            cin >> x >> y;
            Union(x, y);
        }
        set<int> s;
        for (int i = 1; i <= n;i++){
            s.insert(findf(i));
        }
        cout << s.size() << endl;
    }
    return 0;
}

HDU 3038 How Many Answers Are Wrong

大意:

n个数的数组,现在给出m个描述,x y z代表区间x到y的区间和为z

问这些表述中有哪些是错误的

注意有多组输入,但是题目好像没说.....

思路:

区间x y的和为z,也就是说第y个数比第x-1个数大z,那么可以利用带权并查集,维护点到父节点的距离(也就是比父节点大多少)

然后如果输入的两个点是同一个父节点,那么就看两个点到父节点距离的差是否等于z

如果不是一个父节点,那么就要合并父节点

#include <math.h>
#include <stdio.h>
#include <string.h>

#include <algorithm>
#include <cstdio>
#include <iostream>
#include <queue>
#include <set>
#include <stack>
#include <vector>
using namespace std;

const int N = 1e6 + 5;
typedef long long LL;
int n, m;
int f[N], d[N];
int findf(int x) {
    if (x == f[x]) return x;
    int tmp = findf(f[x]);
    d[x] += d[f[x]];
    return f[x] = tmp;
}
void Union(int x, int y, int w) {
    int fx = findf(x);
    int fy = findf(y);
    if (fx != fy) {
        f[fy] = fx;
        d[fy] = w - d[y] + d[x];
    }
}
int res = 0;
int main() {
    while (cin >> n >> m) {
        res = 0;
        for (int i = 0; i <= n; i++) {
            f[i] = i;
            d[i] = 0;
        }
        while (m--) {
            int x, y, z;
            cin >> x >> y >> z;
            x--;
            int fx = findf(x), fy = findf(y);
            if (fx != fy) {
                Union(x, y, z);
            } else {
                if (d[y] - d[x] != z) res++;
            }
        }
        cout << res << endl;
    }

    return 0;
}

POJ 1182 食物链

大意:

有三类动物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),输出假话的总数。

思路:

因为只有三个类型,所以直接可以利用3*n的拓展域并查集,i代表自身种类所在的集合,i+n代表猎物所在集合,i+2n代表天敌所在集合

如果是说法1,那么就将x和y x+n和y+n x+2n和y+2n合并

如果是说法2,那么就将x和y+2n合并,x+n和y合并,x+2n和y+n合并

注意必须是单组输入....

#include <math.h>
#include <stdio.h>
#include <string.h>

#include <algorithm>
#include <cstdio>
#include <iostream>
#include <queue>
#include <set>
#include <stack>
#include <vector>
using namespace std;

const int N = 1e6 + 5;
typedef long long LL;
int n, m;
int f[N], d[N];
int findf(int x) { return x == f[x] ? x : findf(f[x]); }
void Union(int x, int y) {
    int fx = findf(x);
    int fy = findf(y);
    if (fx != fy) {
        f[fy] = fx;
    }
}
int res = 0;
int main() {
    scanf("%d%d",&n,&m);
    res = 0;
    for (int i = 1; i <= 3 * n; i++) {
        f[i] = i;
    }
    while (m--) {
        int d, x, y;
        scanf("%d%d%d",&d,&x,&y);
        if (x > n || y > n) {
            res++;
            continue;
        }
        if (d == 1) {
            int fx = findf(x + n), fy = findf(y);
            if (fx == fy) {
                res++;
                continue;
            }
            fx = findf(x + 2 * n), fy = findf(y);
            if (fx == fy) {
                res++;
                continue;
            }
            Union(x, y);
            Union(x + n, y + n);          //猎物
            Union(x + 2 * n, y + 2 * n);  //天敌
        } else {
            if (x == y) {
                res++;
                continue;
            }
            int fx = findf(x), fy = findf(y);
            if (fx == fy) {  //吃同类
                res++;
                continue;
            }
            fx = findf(x + 2 * n), fy = findf(y);
            if (fx == fy) {  //吃天敌
                res++;
                continue;
            }
            Union(x + n, y);
            Union(x, y + 2 * n);
            Union(x + 2 * n, y + n);
        }
    }
    printf("%d\n",res);

    return 0;
}

POJ 1417 True Liars

大意:

现在有p1个好人 p2个坏人,好人一定说真话,坏人一定说假话

现在给出n句话,格式是x y z,如果z=1,那么代表x说y是好人 ,否则代表x说y是坏人

问能否根据这n句话判断出哪些是好人,能的话从小到大输出哪些是好人

思路:

很容易推出,如果x说y是好人,那么x和y必然同类,否则必然不同类

权值并查集,权值为0代表和父节点同类,1代表和父节点不同类,然后用异或维护即可

但是需要判断能否推出哪些人是好人,由于最后形成的是很多联通块,所以无法直接判断,这就需要利用01背包了

一个物品有两个属性,可以选任意一个属性,最后看能否组成1即可,具体看代码

#include <math.h>
#include <stdio.h>
#include <string.h>

#include <algorithm>
#include <cstdio>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <vector>
using namespace std;

const int N = 1e3 + 5;
typedef long long LL;

int f[N], d[N];
int findf(int x) {
    if (x == f[x]) return x;
    int fx = findf(f[x]);
    d[x] ^= d[f[x]];
    return f[x] = fx;
}
void Union(int x, int y, int diff) {
    int fx = findf(x);
    int fy = findf(y);
    if (fx != fy) {
        f[fy] = fx;
        d[fy] = d[x] ^ d[y] ^ diff;
    }
}
int n, m, p1, p2, vis[N], num[N][2], dp[N][N];
vector<int> a[N][2], res;
int main() {
    while (scanf("%d%d%d", &n, &p1, &p2) && (n + p1 + p2 != 0)) {
        for (int i = 1; i <= 2 * (p1 + p2); i++) {
            f[i] = i;
            a[i][0].clear();
            a[i][1].clear();
            num[i][0] = num[i][1] = 0;
            vis[i] = 0;
            d[i] = 0;
        }
        res.clear();
        while (n--) {
            int x, y;
            char s[5];
            scanf("%d%d%s", &x, &y, &s);
            if (s[0] == 'y') {
                Union(x, y, 0);
            } else {
                Union(x, y, 1);
            }
        }
        int cnt = 0;
        for (int i = 1; i <= (p1 + p2); i++) {
            if (vis[i]) continue;
            int fi = findf(i);
            cnt++;
            for (int j = i; j <= (p1 + p2); j++) {
                int fj = findf(j);
                if (fj == fi) {
                    num[cnt][d[j]]++;
                    a[cnt][d[j]].push_back(j);
                    vis[j] = 1;
                }
            }
        }
        memset(dp, 0, sizeof dp);
        dp[0][0] = 1;
        for (int i = 1; i <= cnt; i++) {
            for (int j = p1; j >= min(num[i][0], num[i][1]); j--) {
                if (j >= num[i][0]) dp[i][j] += dp[i-1][j - num[i][0]];
                if (j >= num[i][1]) dp[i][j] += dp[i-1][j - num[i][1]];
            }
        }
        if (dp[cnt][p1] != 1) {
            cout << "no" << endl;
            continue;
        }
        for (int i = cnt; i >= 1; i--) {
            if (p1 - num[i][0] >= 0 && p2 - num[i][1] >= 0 &&
                dp[i-1][p1 - num[i][0]] == 1) {
                p1 -= num[i][0];
                p2 -= num[i][1];
                for (int j = 0; j < a[i][0].size(); j++)
                    res.push_back(a[i][0][j]);
            } else if (p1 - num[i][1] >= 0 && p2 - num[i][0] >= 0 &&
                       dp[i-1][p1 - num[i][1]] == 1) {
                p1 -= num[i][1];
                p2 -= num[i][0];
                for (int j = 0; j < a[i][1].size(); j++)
                    res.push_back(a[i][1][j]);
            }
        }
        sort(res.begin(), res.end());
        for (int i = 0; i < res.size(); i++) {
            cout << res[i] << endl;
        }
        cout << "end" << endl;
    }

    return 0;
}

POJ 1456 Supermarket

大意:

N个商品,每个商品都有利润p和过期时间d

每天只能卖一个商品,问怎么安排可以收益最大,输出最大收益

思路:

贪心的想,只要在过期时间之前,那么越往后卖越好,这样可以给早过期的商品时间

所以可以利用并查集,维护已经被占用的时间,天数i的父节点fi代表上一个能卖的时间,每次查询\(f[d]\),看是否大于0,如果大于0就和\(f[d]-1\)合并

#include <math.h>
#include <stdio.h>
#include <string.h>

#include <algorithm>
#include <cstdio>
#include <iostream>
#include <queue>
#include <set>
#include <stack>
#include <vector>
using namespace std;

const int N = 1e6 + 5;
typedef long long LL;
int n, m;
int f[N];
int findf(int x) { return x == f[x] ? x : findf(f[x]); }
void Union(int x, int y) {
    int fx = findf(x);
    int fy = findf(y);
    if (fx != fy) {
        f[fy] = fx;
    }
}
int res = 0;
struct node {
    int p, d;
} a[N];
bool cmp(node a, node b) { return a.p > b.p; }
int main() {
    while (scanf("%d", &n) != EOF) {
        for (int i = 0; i < n; i++) {
            cin >> a[i].p >> a[i].d;
        }
        for (int i = 0; i <= 10000; i++) {
            f[i] = i;
        }
        res = 0;
        sort(a, a + n, cmp);
        for (int i = 0; i < n; i++) {
            int fy = findf(a[i].d);
            if(fy>0){
                Union(fy-1, fy);
                res += a[i].p;
            }
        }
        printf("%d\n", res);
    }

    return 0;
}

POJ 1733 Parity game

大意:

现在有一个长度为n的01序列,给出m个说法,代表l到r之间有奇数个还是偶数个1,为前几个说法是自洽的

n的范围为1e9 m的范围为5e5

思路:

带权并查集,用异或维护与父节点的奇偶性关系,如果输入为l到r有偶数个1,那么代表r与l-1的奇偶性相同,否则是不同

因为n很大,所以需要离散化

#include <math.h>
#include <stdio.h>
#include <string.h>

#include <algorithm>
#include <cstdio>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <vector>
using namespace std;

const int N = 5e3 + 5;
typedef long long LL;

int f[N], d[N];
int findf(int x) {
    if (x == f[x]) return x;
    int fx = findf(f[x]);
    d[x] ^= d[f[x]];
    return f[x] = fx;
}
void Union(int x, int y, int diff) {
    int fx = findf(x);
    int fy = findf(y);
    if (fx != fy) {
        f[fy] = fx;
        d[fy] = d[x] ^ d[y] ^ diff;
    }
}

int n, m, t = 0, a[2 * N];
struct node {
    int l, r, ans;
} query[N];
void read_discrete() {
    for (int i = 1; i <= m; ++i) {
        char str[5];
        scanf("%d%d%s", &query[i].l, &query[i].r, str);
        query[i].ans = (str[0] == 'o' ? 1 : 0);
        a[++t] = query[i].l - 1;
        a[++t] = query[i].r;
    }
    sort(a + 1, a + 1 + t);
    n = unique(a + 1, a + 1 + t) - a - 1;  // n <= m
}
int get(int num) { return lower_bound(a + 1, a + n + 1, num) - a; }
int main() {
    cin >> n;
    cin >> m;
    read_discrete();
    for (int i = 0; i <= n; i++) f[i] = i;
    for (int i = 1; i <= m; i++) {
        int x = get(query[i].l - 1);
        int y = get(query[i].r);
        int fx = findf(x), fy = findf(y);
        if (fx == fy) {
            if (d[x] ^ d[y] != query[i].ans) {
                cout << i-1 << endl;
                return 0;
            }
        } else {
            Union(x, y, query[i].ans);
        }
        }
    cout << m << endl;
    return 0;
}

POJ 1984 Navigation Nightmare

大意:

有n个农场,m条路,每条路都连接两个农场,且为纵向或者横向的,每条路的格式为x y d f,代表x在y的f方向,这条路长度为d

现在有q个询问,每个询问的格式为x y t,即只看前t条路的情况下,x和y的曼哈顿距离是多少 不相连的话输出-1

思路:

因为要输出曼哈顿距离,所以不能直接维护一个d数组,应该维护一个横向的数组d1和纵向的数组d2,同时更新这两个数组即可

注意输入时t不是递增的,需要排序,相应的答案也要先存起来,最后依次输出

#include <math.h>
#include <stdio.h>
#include <string.h>

#include <algorithm>
#include <cstdio>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <vector>
using namespace std;

const int N = 5e5 + 5;
typedef long long LL;

int f[N], d1[N],d2[N];
int findf(int x) {
    if (x == f[x]) return x;
    int fx = findf(f[x]);
    d1[x] += d1[f[x]];
    d2[x] += d2[f[x]];
    return f[x] = fx;
}
void Union(int x, int y, int diff1,int diff2) {
    int fx = findf(x);
    int fy = findf(y);
    if (fx != fy) {
        f[fy] = fx;
        d1[fy] = d1[x] - d1[y] + diff1;
        d2[fy] = d2[x] - d2[y] + diff2;
    }
}

int n, m, t = 0, a[2 * N],q;
int res[N];
struct node {
    int x, y, ans;
    char f;
    int id;
} query1[N], query2[N];
bool cmp(node a, node b) { return a.ans < b.ans; }
int main() {
    scanf("%d", &n);
    scanf("%d", &m);
    for (int i = 0; i <= n; i++) f[i] = i;
    for (int i = 1; i <= m; i++) {
        scanf("%d %d %d %c", &query1[i].x, &query1[i].y, &query1[i].ans, &query1[i].f);
    }
    scanf("%d", &q);
    for (int i = 0; i < q; i++) {
        scanf("%d %d %d", &query2[i].x, &query2[i].y, &query2[i].ans);
        query2[i].id = i;
    }
    sort(query2, query2 + q, cmp);
    int now = 1;
    for (int i = 0; i < q; i++) {
        for (int j = now; j <= query2[i].ans; j++) {
            int x = query1[j].x, y = query1[j].y, dis = query1[j].ans;
            if (query1[j].f == 'E') Union(x, y, -dis, 0);
            if (query1[j].f == 'W') Union(x, y, dis, 0);
            if (query1[j].f == 'N') Union(x, y, 0, -dis);
            if (query1[j].f == 'S') Union(x, y, 0, dis);
        }
        now = query2[i].ans;
        int x = query2[i].x, y = query2[i].y;
        int fx = findf(x), fy = findf(y);
        if (fx != fy) {
            res[query2[i].id] = -1;
        } else {
            res[query2[i].id] = abs(d1[x] - d1[y]) + abs(d2[x] - d2[y]);
        }
    }
    for (int i = 0; i < q;i++){
        printf("%d\n", res[i]);
    } 
    return 0;
}

POJ 2492 A Bug's Life

大意:

给出n个小虫,m个关系,每个关系代表两个小虫可以交配,问有没有异常的小虫(同性交配)

思路:

拓展域并查集裸题

#include <math.h>
#include <stdio.h>
#include <string.h>

#include <algorithm>
#include <cstdio>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <vector>
using namespace std;

const int N = 2e3 + 5;
typedef long long LL;

int f[N], d[N];
int findf(int x) { return x == f[x] ? x : f[x] = findf(f[x]); }
void Union(int x, int y) {
    int fx = findf(x);
    int fy = findf(y);
    if (fx != fy) {
        f[fy] = fx;
    }
}
int n, m, t, cases = 0;
vector<int> a[N][2], res;
int main() {
    cin >> t;
    while (t--) {
        scanf("%d%d", &n, &m);
        cases++;
        if (cases != 1) printf("\n");
        int flag = 0;
        for (int i = 1; i <= 2 * n; i++) f[i] = i;
        for (int i = 1; i <= m; i++) {
            int x, y;
            scanf("%d%d", &x, &y);
            int fx = findf(x), fy = findf(y);
            if (fx == fy) {
                flag = 1;
            } else {
                Union(x, y + n);
                Union(x + n, y);
            }
        }
        if (flag)
            printf("Scenario #%d:\nSuspicious bugs found!\n", cases);
        else
            printf("Scenario #%d:\nNo suspicious bugs found!\n", cases);
    }

    return 0;
}

POJ 2912 Rochambeau

大意:

给出n个人,进行剪子包袱锤,给出m次比赛的结果,每个人出的手势是固定的,但是有一个裁判可以随意出,问能否找到这个裁判,并输出在第几次比赛结果后可以判定这个裁判

如果不能找到裁判,那么输出Impossible,如果不能确定哪个是裁判,那么输出Can not determine

n<=500

思路:

枚举裁判,然后判断它是否能成为裁判,即忽略当前枚举的这个人,然后用并查集判断有没有冲突,如果有冲突,那么这个人不是裁判,记录从冲突的行数,然后break(这里写了continue,wa了好久)

如果存在一个可以成为裁判的人(也就是没有冲突)那么输出这个人,而且确定他为裁判的位置是其他所有冲突的行的最大的位置(因为要确定其他的都不是才能确定这个人是裁判)

如果存在两个或以上的人可以成为裁判,那么输出Can not determine

如果一个都没有,那么输出Impossible

#include <math.h>
#include <stdio.h>
#include <string.h>

#include <algorithm>
#include <cstdio>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <vector>
using namespace std;

const int N = 2e4 + 5;
typedef long long LL;

int f[N], d[N];
int findf(int x) { return x == f[x] ? x : f[x] = findf(f[x]); }
void Union(int x, int y) {
    int fx = findf(x);
    int fy = findf(y);
    if (fx != fy) {
        f[fy] = fx;
    }
}
int n, m, t;
struct node {
    int x, y;
    char c;
} a[N];

int main() {
    while (scanf("%d%d", &n, &m) != EOF) {
        int flag = 0;
        for (int i = 1; i <= m; i++) {
            scanf("%d %c %d", &a[i].x, &a[i].c, &a[i].y);
        }
        int line = 0,cnt=0 , res=0;
        for (int i = 0; i < n; i++) {
            int flag = 0;
            for (int j = 0; j < 3 * n; j++) f[j] = j;
            for (int j = 1; j <= m; j++) {
                int x = a[j].x, y = a[j].y;
                char c = a[j].c;
                if (x == i || y == i) continue;
                if (c == '>') {
                    int fx = findf(x);
                    int fy = findf(y + n);
                    if(fx==fy){
                        flag = 1;
                        line = max(line, j);
                        break;
                    }
                    fx = findf(x);
                    fy = findf(y);
                    if(fx==fy){
                        flag = 1;
                        line = max(line, j);
                        break;
                    }
                    Union(x + n, y), Union(x, y + 2 * n), Union(x + 2 * n, y + n);
                }

                if (c == '=') {
                    int fx = findf(x);
                    int fy = findf(y + n);
                    if(fx==fy){
                        flag = 1;
                        line = max(line, j);
                        break;
                    }
                    fx = findf(x+n);
                    fy = findf(y);
                    if(fx==fy){
                        flag = 1;
                        line = max(line, j);
                        break;
                    }
                    Union(x, y), Union(x + 2 * n, y + 2 * n), Union(x + n, y + n);
                }

                if (c == '<') {
                    int fx = findf(x);
                    int fy = findf(y);
                    if(fx==fy){
                        flag = 1;
                        line = max(line, j);
                        break;
                    }
                    fx = findf(x + n);
                    fy = findf(y);
                    if(fx==fy){
                        flag = 1;
                        line = max(line, j);
                        break;
                    }
                    Union(x, y + n), Union(x + 2 * n, y), Union(x + n, y + 2 * n);
                }
            }
            if (flag == 0) cnt++, res = i;
        }
        if(!cnt)printf("Impossible\n");
        else if(cnt>1)printf("Can not determine\n");
        else
            printf("Player %d can be determined to be the judge after %d lines\n", res, line);
    }

    return 0;
}

ZOJ 3261 Connections in Galaxy War

大意:

给出n个星球,m条路,每个星球都需要向能联通的星球中,实力比他强且为联通块中最强的星球求援,最大实力相同则向下标最小的求援

给出q次查询,有两种操作,一种是破坏某一条路,另一种是问这个星球能求援的星球是哪个,没有符合要求的就输出-1

思路:

因为并查集适合处理合并,但是现在是分裂的操作,所以可以反向考虑,首先记录每条边的存活情况,然后将q次查询离线,先将最后仍然存活的边上的点联通,然后倒着查询,遇到一条被破坏的边就将其联通即可

#include <bits/stdc++.h>

using namespace std;

const int N = 1e4 + 5;
typedef long long LL;
int n;

int f[N], num[N], add[N], a[N], m, q;
map<pair<int, int>, bool> use;
int findf(int x) { return x == f[x] ? x : f[x] = findf(f[x]); }
void Union(int x, int y) {
    int fx = findf(x);
    int fy = findf(y);
    if (fx != fy) {
        f[fy] = fx;
        if (num[fy] > num[fx])
            num[fx] = num[fy], add[fx] = add[fy];
        else if (num[fy] == num[fx] && add[fx] > add[fy])
            add[fx] = add[fy];
    }
}
struct node {
    int op, x, y;
} query[5 * N];
struct node2 {
    int u, v;
} edge[2 * N];
int res[5 * N];
int flag = 0;
int main() {
    while (scanf("%d", &n) != EOF) {
        if (!flag)
            flag = 1;
        else
            printf("\n");
        use.clear();
        for (int i = 0; i < n; i++) {
            f[i] = i;
            scanf("%d", &a[i]);
            num[i] = a[i];
            add[i] = i;
        }
        scanf("%d", &m);
        for (int i = 0; i < m; i++) {
            int u, v;
            scanf("%d%d", &u, &v);
            if (u > v) swap(u, v);
            use[{u, v}] = false;
            edge[i].u = u, edge[i].v = v;
        }
        scanf("%d", &q);
        for (int i = 0; i < q; i++) {
            char s[10];
            scanf("%s%d", &s, &query[i].x);
            if (s[0] == 'q')
                query[i].op = 1;
            else {
                scanf("%d", &query[i].y);
                if (query[i].x > query[i].y) swap(query[i].x, query[i].y);
                query[i].op = 0;
                use[{query[i].x, query[i].y}] = true;
            }
        }
        for (int i = 0; i < m; i++) {
            int u = edge[i].u, v = edge[i].v;
            if (!use[{u, v}]) Union(u, v);
        }
        for (int i = q - 1; i >= 0; i--) {
            int op = query[i].op, x = query[i].x, y = query[i].y;
            if (op) {
                int fx = findf(x);
                if (num[fx] > a[x])
                    res[i] = add[fx];
                else
                    res[i] = -1;
            } else
                Union(x, y);
        }
        for (int i = 0; i < q; i++) {
            if (query[i].op) cout << res[i] << endl;
        }
    }
    return 0;
}

HDU 1272 小希的迷宫

大意:

判断一个图是否是联通图,且每个点之间只有一条路可达

思路:

并查集判断即可,比较坑的是子环和空树都是yes...

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 5;
typedef long long LL;
int n;

int f[N], x, y;
map<pair<int, int>, bool> use;
int findf(int x) { return f[x] == x ? x : f[x] = findf(f[x]); }
void Union(int x, int y) {
    int fx = findf(x);
    int fy = findf(y);
    if (fx != fy) {
        f[fy] = fx;
    }
}
int vis[N];
int main() {
    while (scanf("%d%d", &x, &y) != EOF && (x != -1)) {
        int flag = 1;
        for (int i = 1; i <= 100000; i++) f[i] = i, vis[i] = 0;
        if (x == 0) {
            cout << "Yes" << endl;
            continue;
        }
        vis[x] = vis[y] = 1;
        Union(x, y);
        while (scanf("%d%d", &x, &y) && (x + y != 0)) {
            int fx = findf(x), fy = findf(y);
            vis[x] = vis[y] = 1;
            if (x == y) continue;
            if (fx == fy)
                flag = 0;
            else
                Union(x, y);
        }
        int sum = 0;
        for (int i = 1; i <= 100000; i++)
            if (vis[i] && findf(i) == i) sum++;
        if (flag && sum == 1)
            cout << "Yes" << endl;
        else
            cout << "No" << endl;
    }
    return 0;
}

POJ 1308 Is It A Tree?

大意:

和上题类似,不过是判断是否是树,所以不能出现自环、重复边

#include <math.h>
#include <stdio.h>
#include <string.h>

#include <algorithm>
#include <cstdio>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <vector>
using namespace std;

const int N = 1e5 + 5;
typedef long long LL;
int n;

int f[N], x, y;
map<pair<int, int>, bool> use;
int findf(int x) { return f[x] == x ? x : f[x] = findf(f[x]); }
void Union(int x, int y) {
    int fx = findf(x);
    int fy = findf(y);
    if (fx != fy) {
        f[fy] = fx;
    }
}
int vis[N];
int cases = 0;
int main() {
    while (scanf("%d%d", &x, &y) != EOF && (x != -1)) {
        cases++;
        int flag = 1;
        use.clear();
        for (int i = 1; i <= 100000; i++) f[i] = i, vis[i] = 0;
        if (x == 0) {
            printf("Case %d is a tree.\n", cases);
            continue;
        }
        vis[x] = vis[y] = 1;
        pair<int, int> tmp;
        tmp.first = x, tmp.second = y;
        use[tmp] = 1;
        if (x == y)
            flag = 0;
        else
            Union(x, y);
        while (scanf("%d%d", &x, &y) && (x + y != 0)) {
            int fx = findf(x), fy = findf(y);
            pair<int, int> tmp;
            tmp.first = x, tmp.second = y;
            if (use[tmp]) flag = 0;
            vis[x] = vis[y] = 1;
            use[tmp] = 1;
            if (fx == fy)
                flag = 0;
            else
                Union(x, y);
        }
        int sum = 0;
        for (int i = 1; i <= 100000; i++)
            if (vis[i] && findf(i) == i) sum++;
        if (flag && sum == 1)
            printf("Case %d is a tree.\n", cases);
        else
            printf("Case %d is not a tree.\n", cases);
    }
    return 0;
}
posted @ 2021-02-03 11:14  dyhaohaoxuexi  阅读(102)  评论(0编辑  收藏  举报