hdu 3234 Exclusive-OR

Exclusive-OR

並查集的好題
這裏由於每次給定的是xi, xj的關係,並且可以會給定xi的值,所以我們需要3個數組來存儲每個值的信息:v[i]表示i的值,p[i]表示i的父親節點,d[i]表示v[i] ^ v[p[i]] 的值。
  首先是並查集的查詢操作。這裏我們不僅需要壓縮路徑,更新x與根節點的關係,這裏可以由抑或操作的傳遞性直接計算出來,同時如果已知x或者已知樹根的值時,我們要將對應的子節點的值也求出來。這裏僅僅通過抑或操作就可以直接得到節點的值或者樹根的值。每當確定一個節點的數值的時候,我們便要確定該節點所在樹的樹根的值,這樣當對節點x進行查詢操作便可以直接獲得x的值。這一點在合併或者賦值的時候是非常有用的
  對於每個I x y c ,表示v[x]^v[y] = v, 顯然如果對於已知情況,如果兩個點在同一顆樹上,也就是d[x] ^ d[y] == c如果成立,則滿足條件,否則不滿足;如果兩個點不在同一個棵樹上,如果根節點的值都可以得到,那麼只需要d[x] ^ d[y] ^ v[p[x]] ^ v[p[y]] == c滿足則不衝突,否則衝突。如果不知道其中的值,那麼這個時候添加的關係肯定成立,我們只需要將兩棵樹合併,然後計算出樹根之間的關係:d[x] ^ d[y] ^ c. 對於每個l x c, 表示v[x] = c,這時,我們只需要將x的值求出來即可,如果x所在的樹的樹根有值,那麼肯定可以直接計算出x(通過一次並查集的查詢操作)的值,然後比較即可。
  對於計算值,由於數據量比較小隻有15個,暴力即可。利用抑或操作的特性,如果一個數出現偶數次,就相當於沒有出現,只需要維護一個存放根以及對應該樹中節點使用次數的數組。對於每個點,先進行一次查詢操作,然後如果該點有值,那麼將答案直接抑或這個值。否則的話,將此點放到數組中,同時更新節點出現的次數。最後對數組進行掃描,如果有一個樹根節點的出現次數是奇數,說明無法確定答案,否則可以確定答案。

#include <cstdio>
const int maxn = 20000+5;

int p[maxn];
int v[maxn];
int d[maxn];
int count[20][2];
int cnt;
void init(int n){
    for(int i = 0; i < n; i ++){
        p[i] = i;
        v[i] = -1;
        d[i] = 0;
    }
}

int find(int x){
    if(x == p[x])
        return x;
    int r = find(p[x]);
    d[x] = d[p[x]] ^ d[x];
    p[x] = r;

    if(v[x] != -1){
        v[r] = v[x] ^ d[x];
    }
    if(v[r] != -1){
        v[x] = v[r] ^ d[x];
    }
    return r;
}

bool joint(int x, int y, int c){
    int px = find(x);
    int py = find(y);

    if(px == py){
        if((d[x] ^ d[y]) != c){
            return false;
        }
    }
    if(v[px] != -1 && v[py] != -1){
        if((v[px] ^ v[py] ^ d[x] ^ d[y] ^ c) != 0){
            return false;
        }
    }
    else{
        p[px] = py;
        d[px] = d[x] ^ d[y] ^ c;
        if(v[px] != -1){
            v[py] = v[px] ^ d[px];
        }
        else if(v[py] != -1){
            v[px] = v[py] ^ d[px];
        }
    }
    return true;
}
//如果將x的值設置爲c
bool set(int x, int c){
    find(x);
    if(v[x] != -1){
        if(v[x] != c)
            return false;
    }
    else{
        v[x] = c;
        v[p[x]] = d[x] ^ c;
    }
    return true;
}

void insert(int x){
    int i;
    for(i = 0; i < cnt; i ++){
        if(count[i][0] == p[x]){
            count[i][1] ++;
            break;
        }
    }
    if(i==cnt){
        count[cnt][0] = p[x];
        count[cnt][1] = 1;
        cnt ++;
    }
}
bool check(){
    for(int i = 0; i < cnt; i ++){
        if(count[i][1]&1){
            return false;
        }
    }
    return true;
}
int main(){
    int n, Q, x, y, c, ins, cas, ans;
    bool silence, flag;
    char cmd[2];
    cas = 0;
    //freopen("data.in","r", stdin);
    while(scanf("%d%d", &n, &Q), n||Q){
        printf("Case %d:\n", ++ cas);
        init(n);
        silence = false;
        flag = true;
        ins = 0;

        while(Q--){
            scanf("%s", cmd);
            if(cmd[0]=='I'){
                ins ++;
                scanf("%d%d", &x, &y);
                cmd[0] = getchar();
                if(cmd[0]== ' '){
                    scanf("%d", &c);
                    if(silence)
                        continue;
                    if(!joint(x, y, c)){
                        silence = true;
                        printf("The first %d facts are conflicting.\n", ins);
                    }
                }
                else{
                    if(silence)
                        continue;
                    if(!set(x, y)){
                        silence = true;
                        printf("The first %d facts are conflicting.\n", ins);
                    }
                }
            }
            else{
                scanf("%d", &c);
                ans = 0;
                cnt = 0;
                flag = false;
                while(c--){
                    scanf("%d", &x);
                    find(x);
                    if(v[x] != -1){
                        ans = ans ^ v[x];
                    }
                    else{
                        ans = ans ^ d[x];
                        insert(x);
                    }
                }
                if(silence){
                    continue;
                }
                flag = check();

                if(flag){
                    printf("%d\n", ans);
                }
                else{
                    printf("I don't know.\n");
                }
            }
        }
        printf("\n");
    }
    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章