[kuangbin帶你飛]專題五 並查集——題解


並查集在查詢連通關係上具有很大的作用,本身的代碼很短,實現很容易,通過訪問父節點直到父節點爲本自身時即爲訪問到該點的祖先節點,使用f[x]=F(f[x])可以在查詢一個點的祖先節點的同時,把路徑上的所有點直接連接到祖先節點上,從而下次可以O(1)查詢該點的祖先節點,在判斷連通圖連通與否等類似問題上具有很大的用處。

並查集可以在保存父節點f[]同時開另一個數組附加其他信息,如保存點到父節點的的距離,點與父節點的關係(同類與否,食物鏈關係),有附加信息的情況下,在使用F(x)來查詢結點祖先結點的同時,要同時更新f[]數組和附加信息數組,以保證附加信息始終是結點對於父節點的信息。


A - Wireless Network POJ - 2236

二維平面上的並查集,預處理出點與點間的距離,每次有電腦修復就把與其距離小於等於d的點連通起來,查詢只要祖先節點相同就是連通的了。

#include<iostream>
#include<algorithm>
#include<string.h>
#include<stdio.h>
#include<vector>
#include<string>
#include<cmath>
#include<set>
#include<queue>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
const int mod = 1000000007;
const int maxm = 1005;
const int maxn = 1005;
int n, m;
pair<int, int> point[maxn];
int dis[maxn][maxn];
bool fix[maxn];
int f[maxn];
int F(int x){
    if (f[x] == x)return x;
    return f[x] = F(f[x]);
}
int main() {
    int x, y;
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; i++){
        f[i]=i;
        scanf("%d%d", &point[i].first, &point[i].second);
    }
    for (int i = 0; i < n; i++){
        for (int j = i + 1; j < n; j++){
            dis[j][i] = dis[i][j] = pow(abs(point[i].first - point[j].first), 2) + pow(abs(point[i].second - point[j].second), 2);
        }
    }
    char str[5];
    while (~scanf("%s", str)){
        if (str[0] == 'O'){
            scanf("%d", &x);
            x--;
            fix[x] = 1;
            for (int i = 0; i < n; i++){
                if (fix[i]&&dis[x][i]<=m*m){
                    f[F(i)] = F(x);
                }
            }
        }
        else{
            scanf("%d%d", &x, &y);
            x--, y--;
            if (F(x) == F(y)){
                printf("SUCCESS\n");
            }
            else{
                printf("FAIL\n");
            }
        }
    }
    return 0;
}

B - The Suspects POJ - 1611

比較基礎的並查集題目,只要把同一個社團裏的同學連通起來,最後與0號同學在同一個並查集裏的人的數量就是答案。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<set>
using namespace std;
typedef long long ll;
const int maxn = 30005;
const int mod = 1000000009;
int n,m;
int f[maxn];
int F(int x){
    if (f[x] == x)return x;
    return f[x] = F(f[x]);
}

int main(){
    int x,head,y;
    while (~scanf("%d%d", &n, &m), n + m){
        for (int i = 0; i < n; i++)f[i] = i;
        for (int i = 0; i < m; i++){
            scanf("%d", &x);
            if (x == 0)continue;
            scanf("%d", &head);
            for (int i = 1; i < x; i++){
                scanf("%d", &y);
                f[F(y)] = F(head);
            }
        }
        int gg = F(0);
        int ans = 0;
        for (int i = 0; i < n; i++){
            if (F(i) == gg){ ans++; }
        }
        printf("%d\n", ans);
    }
    return 0;
}

C - How Many Tables HDU - 1213

就是找連通塊的數量。

#include<iostream>
#include<algorithm>
#include<string.h>
#include<stdio.h>
#include<vector>
#include<string>
#include<cmath>
#include<set>
#include<map>
#include<queue>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
const int mod = 1000000007;
const int maxm = 50000005;
const int maxn = 1005;
const int M = 25;
int n, m;
int f[maxn];
int F(int x){
    if (f[x] == x){ return x; }
    return f[x] = F(f[x]);
}
int main() {
    int t,x,y;
    scanf("%d", &t);
    while (t--){
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; i++){
            f[i] = i;
        }
        for (int i = 0; i < m; i++){
            scanf("%d%d", &x, &y);
            f[F(x)] = F(y);
        }
        int ans = 0;
        for (int i = 1; i <= n; i++){
            if (f[i] == i){ ans++; }
        }
        printf("%d\n", ans);
    }
    return 0;
}

How Many Answers Are Wrong HDU - 3038

這題並查集維護的東西是數軸上的點,在開f[]數組的同時還要開一個val[]數組保存點到父節點這段區間的數值之和,有遞增的三個點abc,如果知道了ac區間的數值和以及ab直接的數值和,那麼bc之間的數值和就可以得出來了,這就可以轉換成並查集的模型了,如果兩個點不在同一個並查集裏,那麼這兩個點構成的區間的數值和也就還沒有約束,否則就已經明確約束了該區間的值。

要注意維護並查集要同一以左端的結點爲父節點或者右端的結點爲父節點。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<string.h>
#include<set>
using namespace std;
typedef long long ll;
const int maxn = 200005;
const int mod = 1000000009;
int n,m;
int f[maxn];
int val[maxn];
int F(int x){
    if (f[x] == x)return x;
    int tt = F(f[x]);
    val[x] += val[f[x]];
    return f[x] = tt;
}

int main(){
    int u, v, w;
    while (~scanf("%d%d", &n, &m)){
        for (int i = 0; i <= n; i++)f[i] = i;
        memset(val, 0, sizeof(val));
        int ans = 0;
        for (int i = 0; i < m; i++){
            scanf("%d%d%d", &u, &v, &w);
            u--;
            int f1 = F(u);
            int f2 = F(v);
            if (f1 != f2){
                f[f2] = f1;
                val[f2] = val[u] - val[v] + w;
            }
            else{
                if (val[v] - val[u] != w)ans++;
            }
        }
        printf("%d\n", ans);  
    }
    return 0;
}

E - 食物鏈 POJ - 1182

很經典的題目,這題的並查集附加的信息是點與父節點的關係是(同類,吃,被吃),分別帶入數值0,1,2,那麼在mod3的情況下就可以直接用加減法更新附加信息(a吃b[1]+b吃c[1]=c被a吃[2]),運算上要注意。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<string.h>
#include<set>
using namespace std;
typedef long long ll;
const int maxn = 50005;
const int mod = 1000000009;
int n, m;
int f[maxn];
int relation[maxn];
int F(int x){
    if (f[x] == x)return x;
    int tmp = F(f[x]);
    relation[x] = (relation[x] + relation[f[x]]) % 3;
    return f[x] = tmp;
}

int main(){
    int op, x, y;
    scanf("%d%d", &n, &m);
    int ans = 0;
    memset(relation, 0, sizeof(relation));
    for (int i = 1; i <= n; i++){ f[i] = i; }
    for (int i = 0; i < m; i++){
        scanf("%d%d%d", &op, &x, &y);
        if (x>n || y > n){ ans++; continue; }
        if (op == 2 && x == y){ ans++; continue; }
        int f1 = F(x), f2 = F(y);
        if (op == 1){
            if (f1 == f2){
                if (relation[x] != relation[y]){ ans++; continue; }
            }
            else{
                f[f1] = f2;
                relation[f1] = (-relation[x] + relation[y] + 3) % 3;
            }
        }
        else{//x吃y
            if (f1 == f2){
                if (((relation[x] - relation[y] + 3) % 3) != 1){ ans++; continue; }
            }
            else{
                f[f1] = f2;
                relation[f1] = (-relation[x] + relation[y] + 1 + 3) % 3;
            }
        }
    }
    printf("%d\n", ans);
    return 0;
}

True Liars POJ - 1417

十分綜合的一道題目,並查集+揹包DP+類似路徑記錄,對於任意a說b爲yes的句子,ab就爲同類,爲no的句子,ab即爲對立類,通過這樣的關係可以構成一個個連通塊,每一個連通塊的的結點分爲兩類(正方&反方),兩類與祖先節點的關係分別爲同類和對立類(用附加信息數組維護),然後用dp對每一個並查集選擇正方或者反方,求出正方爲m人的方案數,若方案數爲1,則能確定那些人爲正方,哪些人爲反方。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<string.h>
using namespace std;
const int maxn = 605;
int n, m, k;

struct pp{
    int fa, relat;
    int same, other;
    int ttt;
}f[maxn];

int dp[maxn][maxn];

int F(int x){
    if (f[x].fa == x)return x;
    int tt = F(f[x].fa);
    f[x].relat = (f[x].relat + f[f[x].fa].relat) % 2;
    return f[x].fa = tt;
}
char str[10];
int head[maxn];
int main(){
    int x, y, z;
    while (scanf("%d%d%d", &n,&m,&k), n+m+k){
        memset(head, 0, sizeof(head));
        for (int i = 1; i <= m+k; i++){
            f[i].fa = i;
            f[i].other = f[i].relat = f[i].ttt = 0;
            f[i].same = 1;
        }
        bool ok = 0;
        int d;
        for (int i = 0; i < n; i++){
            scanf("%d%d%s", &x, &y, str);
            if (ok)continue;
            int lf = F(x), rf = F(y);
            if (str[0] == 'y')d = 0;
            else d = 1;
            if (lf == rf && (f[x].relat + f[y].relat) % 2 != d)ok = 1;
            else if (lf != rf){
                f[lf].fa = rf;
                f[lf].relat = (f[x].relat + f[y].relat + d) % 2;
            }
        }
        int tot = 1;
        if (!ok){
            for (int i = 1; i <= m+k; i++){
                int uu = F(i);
                if (uu == i)head[tot++]=i;
                else{
                    f[uu].other += f[i].relat;
                    f[uu].same += 1 - f[i].relat;
                }
            }
        memset(dp, 0, sizeof(dp));
        dp[1][f[head[1]].same] += 1;
        dp[1][f[head[1]].other] += 1;

        for (int i = 2; i < tot; i++){
            int uu = head[i];
            for (int j = 0; j <= m+k; j++){
                if (dp[i - 1][j]){
                    dp[i][j + f[uu].same] += dp[i - 1][j];
                    dp[i][j + f[uu].other] += dp[i - 1][j];
                }
            }
        }
        }
        if (dp[tot - 1][k] != 1 || ok)printf("no\n");
        else{
            int mm = m;
            for (int i = tot - 1; i > 0; i--){
                int uu = head[i];
                int vv = f[uu].same;
                if (i != 1 && dp[i - 1][mm - vv] !=0 || (i == 1 && mm == vv)){
                    f[uu].ttt = 1;
                    mm -= vv;
                }
                else{
                    mm-= f[uu].other;
                }
            }

        for (int i = 1; i <= m+k; i++){
            int uu = F(i);
            if (f[uu].ttt&&f[i].relat == 0 || f[uu].ttt == 0 && f[i].relat){
                printf("%d\n", i);
            }
        }
        printf("end\n");
    }
    }

    return 0;
}

G - Supermarket POJ - 1456

這題我用優先隊列逆着掃一遍實現的,並不是很懂怎麼用並查集做。

#include<iostream>
#include<cstdio>
#include<string>
#include<string.h>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn = 10005;
int n;
int val[maxn];
struct pp{
    int val, time;
}p[maxn];


bool cmp(pp a, pp b){
    return a.time > b.time;
}



int main(){
    while (~scanf("%d", &n)){
        for (int i = 0; i < n; i++){
            scanf("%d%d", &p[i].val, &p[i].time);
        }
        int ans = 0;
        sort(p, p + n, cmp);
        priority_queue<int> que;
        int cnt = 0;
        for (int i = 10000; i >= 1; i--){
            while (p[cnt].time >= i){ que.push(p[cnt++].val); }
            if (!que.empty()){
                ans += que.top();
                que.pop();
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}

H - Parity game POJ - 1733

跟那題區間數值和的很像,不過要離散化,離散化不能把不相鄰的點離散到相鄰,否則本來不在同一個連通塊的就會在同一個連通塊裏了。因此,要嘛就是離散化之前把左端結點先-1(而不是離散化後再減)(注意給出的結點好像不一定是第一個小),要嘛離散化對於不同的點+2。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<string.h>
#include<set>
using namespace std;
typedef long long ll;
const int maxn = 1000005;
const int mod = 1000000009;
int n, m;
struct dd{
    int num, id;
    bool l;
}d[maxn];
int xx[maxn], yy[maxn], zz[maxn];
int f[maxn];
int ii[maxn];
int F(int x){
    if (f[x] == x)return x;
    int tt = F(f[x]);
    ii[x] = (ii[x] + ii[f[x]]) % 2;
    return f[x] = tt;
}
char str[66];
bool cmp(dd a, dd b){
    return a.num < b.num;
}
int main(){
    scanf("%d", &n);
    for (int i = 0; i < maxn; i++){ f[i] = i; ii[i] = 0; }
    scanf("%d", &m);
    bool flag = 0;
    int ans = m;
    int tot = 0;
    for (int i = 0; i < m; i++){
        scanf("%d%d%s", &d[tot].num, &d[tot + 1].num, str);
        if (d[tot].num>d[tot + 1].num)swap(d[tot].num, d[tot + 1].num);
        d[tot].num--;
        d[tot].id = d[tot + 1].id = i;
        d[tot].l = 1; d[tot + 1].l = 0;
        tot += 2;
        zz[i] = str[0] == 'e' ? 0 : 1;
    }
    sort(d, d + tot, cmp);
    int cnt = d[0].num;
    int ttt = 0;
    for (int i = 0; i < tot; i++){
        if (d[i].num != cnt){ cnt = d[i].num; ttt += 1; }
        if (d[i].l){ xx[d[i].id] = ttt; }
        else{ yy[d[i].id] = ttt; }
    }

    for (int i = 0; i < m; i++){
        int x = xx[i], y = yy[i], z = zz[i];
        if (flag)continue;
        int f1 = F(x), f2 = F(y);
        if (f1 == f2){
            if ((ii[y] - ii[x] + 2) % 2 != z){ ans = i; flag = 1; continue; }
        }
        else{
            f[f2] = f1;
            ii[f2] = (ii[x] + z - ii[y] + 2) % 2;
        }
    }
    printf("%d", ans);
    return 0;
}

I - Navigation Nightmare POJ - 1984

維護平面上的距離的並查集,模型很好理解,但是更新維護附加數組dis[]要十分小心。

#include<iostream>
#include<string.h>
#include<algorithm>
#include<stdio.h>
#include<cmath>
#include<string>
#include<stack>
#include<vector>
#define ll long long
using namespace std;
const int mod = 1000000007;
const int maxm = 40005;
const int maxn = 40005;
const int maxk = 10005;
const int inf = 0x3f3f3f3f;
int n, m,k;
int f[maxn];
int disx[maxn], disy[maxn];
int ans[maxk];
struct messg{
    int u, v, l;
    char dir;
}mes[maxm];
struct query{
    int u, v, idx,id;
    bool operator<(const query b)const{
        return idx < b.idx;
    }
}id[maxk];

int F(int x){
    if (f[x] == x)return x;
    int ff = f[x];
    int cc = F(f[x]);
    disx[x] += disx[ff];
    disy[x] += disy[ff];
    return f[x] = cc;
}

int main(){
    while (~scanf("%d%d", &n, &m)){
        for (int i = 1; i <= n; i++){ f[i] = i; }
        memset(disx, 0, sizeof(disx));
        memset(disy, 0, sizeof(disy));
        for (int i = 0; i < m; i++){
            scanf("%d%d%d%s", &mes[i].u, &mes[i].v, &mes[i].l, &mes[i].dir);
        }
        scanf("%d", &k);
        for (int i = 0; i < k; i++){
            scanf("%d%d%d", &id[i].u, &id[i].v, &id[i].idx);
            id[i].id = i;
        }
        sort(id, id + k);
        int cnt = 0;
        for (int i = 0; i < k; i++){
            while (cnt < id[i].idx){
                messg cur = mes[cnt];
                int f1 = F(cur.u);
                int f2 = F(cur.v);
                f[f1] = f2;
                disx[f1] = disx[cur.v]-disx[cur.u];
                disy[f1] = disy[cur.v]-disy[cur.u];
                if (cur.dir == 'N')disy[f1] += cur.l;
                else if (cur.dir == 'S')disy[f1] -= cur.l;
                else if (cur.dir == 'W')disx[f1] += cur.l;
                else disx[f1] -= cur.l;
                cnt++;
            }
            if (F(id[i].u) != F(id[i].v)){ ans[id[i].id] = -1; }
            else{
                int a = abs(disx[id[i].u] - disx[id[i].v]);
                a += abs(disy[id[i].u] - disy[id[i].v]);
                ans[id[i].id] = a;
            }
        }
        for (int i = 0; i < k; i++){ printf("%d\n", ans[i]); }
    }
    return 0;
}

J - A Bug’s Life POJ - 2492

找有沒有bug會和兩種性別的bug接觸,我用的二分圖染色的方法做的,如果沒有特殊的bug,得到的就會是一張二分圖,不過用並查集的方法一樣很好理解,維護附加數組,並查集裏的任何三隻蟲必定有兩隻以上是同性別(不會接觸),如果三隻都互相接觸過就存在一隻特別的bug。

#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<iostream>
#include<stack>
#include<string>
#include<vector>
using namespace std;
int n,m;
bool ans;
int color[2005];
vector<int> e[2005];

bool bipart(int cnt){
    for(int i=0;i<e[cnt].size();i++){
        int v=e[cnt][i];
        if(!color[v]){
            color[v]=3-color[cnt];
            if(!bipart(v))return 0;
        }
        else if(color[v]==color[cnt]){return 0;}
    }
    return 1;
}

int main(){
    int t,x,y;
    int cas=0;
    scanf("%d",&t);
    while(t--){
        ans=1;
        memset(color,0,sizeof(color));
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){e[i].clear();}
        for(int i=0;i<m;i++){
            scanf("%d%d",&x,&y);
            e[x].push_back(y);
            e[y].push_back(x);
        }
        for(int i=1;i<=n;i++){
            if(!color[i]){
                color[i]=1;
                if(!bipart(i)){
                    ans=0;break;
                }
            }
        }
        printf("Scenario #%d:\n",++cas);
        if(ans==0){printf("Suspicious bugs found!\n");}
        else {printf("No suspicious bugs found!\n");}
        if(t!=0){printf("\n");}
    }
    return 0;
}

K - Rochambeau POJ - 2912

模型跟食物鏈那題是一樣的,有三個類別,我們要找出一個judge,他在剪刀石頭布種會隨機出,而其他玩家每個類別只會出固定的一種,枚舉裁判,無視有裁判參與的回合,若有衝突則這個人有可能是裁判。

#include<iostream>
#include<cstdio>
#include<string>
#include<string.h>
#include<algorithm>
#include<queue>
#include<map>
#include<set>
using namespace std;
const int maxn = 5005;
int n,m,q;
int f[maxn];
int vv[maxn];
int error[maxn];
struct kk{
    int l, r,sta;
}k[maxn];

int F(int x){
    if (f[x] == x)return x;
    int tt = F(f[x]);
    vv[x] = (vv[x] + vv[f[x]]) % 3;
    return f[x] = tt;
}
void init(){
    for (int i = 0; i < n; i++)f[i] = i;
    memset(vv, 0, sizeof(vv));
}

int main(){
    while (~scanf("%d%d", &n, &m)){
        init();
        for (int i = 0; i < m; i++){
            scanf("%d", &k[i].l);
            char c;
            c = getchar();
            scanf("%d", &k[i].r);
            k[i].sta = (c == '=' ? 0 : (c == '<' ? 2 : 1));
        }
        memset(error, -1, sizeof(error));
        for (int i = 0; i < n; i++){
            init();
            for (int j = 0; j < m; j++){
                int aa = k[j].l, bb = k[j].r;
                if (i == aa || i ==bb)continue;
                int ra = F(aa), rb = F(bb);
                if (ra == rb){
                    if (k[j].sta == 0 && vv[aa] != vv[bb]){ error[i] = j + 1; break; }
                    else if (k[j].sta == 2 && (vv[aa] - vv[bb] + 3) % 3 != 2){ error[i] = j + 1; break; }
                    else if (k[j].sta == 1 && (vv[aa] - vv[bb] + 3) % 3 != 1){ error[i] = j + 1; break; }
                }
                else{
                    f[ra] = rb;
                    vv[ra] = (-vv[aa]+k[j].sta+vv[bb]+3) % 3;
                }
            }
        }
        int cnt = 0, a1 = 0, a2 = 0;
        for (int i = 0; i < n; i++){
            if (error[i] == -1){
                cnt++;
                a1 = i;
            }
            a2 = max(a2, error[i]);
        }
        if (cnt == 0)puts("Impossible");
        else if (cnt>1)puts("Can not determine");
        else printf("Player %d can be determined to be the judge after %d lines\n", a1, a2);
    }
    return 0;
}

Connections in Galaxy War ZOJ - 3261

很好的一道題,並查集因爲只能有連通的操作而不能有斷開連通的操作,所以這題必須反向來做,其與的就是簡單的處理f[]數組,圍護並查集父節點要求爲power值最大且結點序號最小的那一個。

#include<iostream>
#include<cstdio>
#include<string>
#include<string.h>
#include<algorithm>
#include<queue>
#include<map>
#include<set>
using namespace std;
const int maxn = 10005;
int n,m,q;
int f[maxn];
int val[maxn];
set<pair<int, int> >s;
pair<int, int>  s1[2 * maxn];
pair<int, int> s2[5 * maxn];
char str[5 * maxn][10];
int F(int x){
    if (f[x] == x)return x;
    return f[x] = F(f[x]);
}
int main(){
    int x, y;
    int cas = 0;
    while (~scanf("%d", &n)){
        if (cas){
            printf("\n");
        }
        else{ cas++; }
        s.clear();
        for (int i = 0; i < n; i++){ f[i] = i; }
        for (int i = 0; i < n; i++){
            scanf("%d", &val[i]);
        }
        scanf("%d", &m);
        for (int i = 0; i < m; i++){
            scanf("%d%d", &s1[i].first,&s1[i].second);
        }
        scanf("%d", &q);
        for (int i = 0; i < q; i++){
            scanf("%s", str[i]);
            if (str[i][0] == 'd'){
                scanf("%d%d", &s2[i].first, &s2[i].second);
                s.insert({ s2[i].first, s2[i].second });
            }
            else{
                scanf("%d", &s2[i].first);
            }
        }
        for (int i = 0; i < m; i++){
            int aa = s1[i].first, bb = s1[i].second;
            if (s.count({ aa,bb }) == 0 && s.count({ bb, aa }) == 0){
                int l = F(aa), r = F(bb);
                int ml = val[l], mr = val[r];
                if (ml>mr || (ml == mr&&l <= r)){
                    f[r] = l;
                }
                else{
                    f[l] = r;
                }
            }
        }
        vector<int > ans;
        for (int i = q - 1; i >= 0; i--){
            if (str[i][0] == 'd'){
                int l = F(s2[i].first), r = F(s2[i].second);
                int ml = val[F(l)], mr = val[F(r)];
                if (ml>mr || (ml == mr&&l <= r)){
                    f[r] = l;
                }
                else{
                    f[l] = r;
                }
            }
            else{
                int cur = s2[i].first;
                int vvv = val[F(cur)];
                if (vvv > val[cur]){
                    ans.push_back(f[cur]);
                }
                else{
                    ans.push_back(-1);
                }
            }
        }
        int ttt = ans.size();
        for (int i = ttt - 1; i >= 0; i--){
            printf("%d\n", ans[i]);
        }
    }
    return 0;
}

M - 小希的迷宮 HDU - 1272

圖上的並查集,最爲明顯的模型了,連接結點是如果兩個結點已經在同一個連通塊種的話,那麼迷宮就不成立了,並且還要要求只有一個連通塊。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<string.h>
#include<set>
using namespace std;
typedef long long ll;
const int maxn = 100005;
const int mod = 1000000009;
int n, m;
int f[maxn];
bool vis[maxn];
int F(int x){
    if (f[x] == x)return x;
    return f[x] = F(f[x]);
}
int x, y;
int main(){
    while (scanf("%d%d", &x, &y), x != -1){
        if (x == 0 && y == 0){ printf("Yes\n"); continue; }
        memset(vis, 0, sizeof(vis));
        for (int i = 1; i <= 100000; i++)f[i] = i;
        f[x] = y;
        vis[x] = vis[y] = 1;
        bool flag = 0;
        while (scanf("%d%d", &x, &y), x + y){
            if (flag)continue;
            if (F(x) == F(y)){ flag = 1; continue; }
            vis[x] = vis[y] = 1;
            f[F(x)] = F(y);
        }
        int ff = -1;
        if (flag == 0){
            for (int i = 1; i <= 100000; i++){
                if (vis[i]){
                    if (ff == -1)ff = F(i);
                    else{
                        if (F(i) != ff){ flag = 1; break; }
                    }
                }
            }
        }
        if (flag)printf("No\n");
        else printf("Yes\n");
    }

    return 0;
}

N - Is It A Tree? POJ - 1308

跟上一個的模型很類似,不過邊變成了有向邊,求圖是否爲樹,除了上一題的條件之外,對於每一個操作a連接一條有向邊到b,b只能是葉子結點或者是一個連通塊的祖先節點,否則肯定不是一棵樹有向樹。

#include<iostream>
#include<cstdio>
#include<string>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn = 100005;
int f[maxn];
bool vis[maxn];
int F(int x){
    if (f[x] == x)return x;
    return f[x] = F(f[x]);
}
int n, m;
int main(){
    int cas = 0;
    while (scanf("%d%d", &n, &m),n!=-1){
        memset(vis, 0, sizeof(vis));
        cas++;
        bool flag = 1;
        for (int i = 1; i < maxn; i++)f[i] = i;
        if (n + m == 0){ printf("Case %d is a tree.\n",cas); continue; }
        if (m == n)flag = 0;
        f[m] = n;
        vis[m] = vis[n] = 1;
        while (scanf("%d%d", &n, &m),n+m){
            if (flag == 0)continue;
            int f1 = F(n), f2 = F(m);
            vis[n] = vis[m] = 1;
            if (f2 != m||f1==f2){ flag = 0; continue; }
            f[f2] = f1;
        }
        if (flag){
            int ff = -1;
            for (int i = 1; i < maxn; i++){
                if (vis[i]){
                    if (ff == -1)ff = F(i);
                    else{
                        if (F(i) != ff){ flag = 0; break; }
                    }
                }
            }
        }
        if (flag)printf("Case %d is a tree.\n", cas);
        else printf("Case %d is not a tree.\n", cas);
    }
    return 0;
}

要打省賽了,要好好加油啊。


發佈了113 篇原創文章 · 獲贊 41 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章