HDU 6625 three arrays 求兩個序列異或最小值的排列(一個可以推廣的正解

@(hdu 6625求兩個序列異或最小值的排列)

題意:

\(T(100)\)組,每組兩個長度爲\(n(100000)\)的排列,你可以將\(a[]\)\(b[]\)隨機排列,可以得到\(c[i]=a[i]\)^\(b[i]\),求字典序最小的\(c[]\)

解析

一個顯然對的貪心做法:
針對本題

  • 每次兩顆字典樹同時往下走,如果都有\(0\)或者\(1\)這條路徑,就隨便同時走\(0\;or\;1\)這條路徑,否則只能一個走\(0\),一個走\(1\)。這樣複雜度是嚴格\(O(log)\)的,最後將得到的\(n\)個數字排序即爲最後答案。
  • 這樣爲什麼正確呢?
  • 如果當前兩字典樹都有\(0\)\(1\)的路徑,同時走\(0\)這條路得到數字肯定不能保證是當前能異或出來的最小值,但是可以肯定的是他一定是字典序最小的序列所包含的某個值。
  • 如果想單純的求兩個01字典樹異或最小值,個人感覺還沒有較好的複雜度的做法。

一個可以推廣的正解:

  • 出題人\(dreamoon\)提供的正解:
  • 現在\(a[]\)中隨便找一個數字\(x\),然後在\(b[]\)中相應找一個和\(x\)匹配異或最小的數字\(y\),再在\(a[]\)裏面找一個和\(y\)匹配最小的數字\(z\)遞歸下去一定會找到一個大小爲2的環
  • 把這個環這兩個數字取出來,再回到上一個失配位置繼續遞歸下去。
  • 這樣得到的\(n\)個數字排序後即爲最終答案。
  • 複雜度同樣很科學並且這個思路適用性很廣。

Code1

const int MXN = 1e5 + 7;
const int MXE = 2e6 + 7;
int n, m;
int ar[MXN], br[MXN];
struct Trie {
    int tot;
    int nex[MXE][2], num[MXE], val[MXE];
    Trie(){nex[0][0] = nex[0][1] = -1;}
    void newnode() {
        ++ tot;
        nex[tot][0] = nex[tot][1] = -1;
    }
    void inisert(int x) {
        int rt = 0;
        for(int i = 31, tmp; i >= 0; --i) {
            tmp = ((x>>i)&1);
            if(nex[rt][tmp] == -1) newnode(), nex[rt][tmp] = tot;
            rt = nex[rt][tmp];
            num[rt] ++;
        }
        val[rt] = x;
    }
    void del(int x) {
        int rt = 0;
        for(int i = 31, tmp; i >= 0; --i) {
            tmp = ((x>>i)&1);
            int lst = rt;
            rt = nex[rt][tmp];
            nex[lst][tmp] = -1;
            num[rt] = 0;
        }
    }
}cw[2];
bool check(int id, int rt, int tmp) {
    return cw[id].nex[rt][tmp] != -1 && cw[id].num[cw[id].nex[rt][tmp]] > 0;
}
int getans() {
    int rt1 = 0, rt2 = 0;
    for(int i = 31; i >= 0; --i) {
        if(check(0, rt1, 0) && check(1, rt2, 0)) {
            rt1 = cw[0].nex[rt1][0];
            rt2 = cw[1].nex[rt2][0];
            -- cw[0].num[rt1];
            -- cw[1].num[rt2];
        }else if(check(0, rt1, 1) && check(1, rt2, 1)) {
            rt1 = cw[0].nex[rt1][1];
            rt2 = cw[1].nex[rt2][1];
            -- cw[0].num[rt1];
            -- cw[1].num[rt2];
        }else if(check(0, rt1, 1) && check(1, rt2, 0)) {
            rt1 = cw[0].nex[rt1][1];
            rt2 = cw[1].nex[rt2][0];
            -- cw[0].num[rt1];
            -- cw[1].num[rt2];
        }else if(check(0, rt1, 0) && check(1, rt2, 1)) {
            rt1 = cw[0].nex[rt1][0];
            rt2 = cw[1].nex[rt2][1];
            -- cw[0].num[rt1];
            -- cw[1].num[rt2];
        }
    }
    return cw[0].val[rt1] ^ cw[1].val[rt2];
}
int main() {
#ifndef ONLINE_JUDGE
    freopen("/home/cwolf9/CLionProjects/ccc/in.txt", "r", stdin);
//    freopen("/home/cwolf9/CLionProjects/ccc/out.txt", "w", stdout);
#endif
    int tim = read();
    while(tim --) {
        n = read();
        cw[0].tot = cw[1].tot = 0;
        for(int i = 1; i <= n; ++i) ar[i] = read(), cw[0].inisert(ar[i]);
        for(int i = 1; i <= n; ++i) br[i] = read(), cw[1].inisert(br[i]);
        vector<int> vs;
        for(int i = 1; i <= n; ++i) vs.eb(getans());
        sort(all(vs));
        for(int i = 0; i < SZ(vs); ++i) printf("%d%c", vs[i], " \n"[i == SZ(vs) - 1]);
        for(int i = 1; i <= n; ++i) cw[0].del(ar[i]), cw[1].del(br[i]);
    }
    return 0;
}

Code2

const int MXN = 1e5 + 7;
const int MXE = 2e6 + 7;
int n, m;
int ar[MXN], br[MXN];
struct Trie {
    int tot;
    int nex[MXE][2], num[MXE], val[MXE];
    Trie(){nex[0][0] = nex[0][1] = -1;}
    void newnode() {
        ++ tot;
        nex[tot][0] = nex[tot][1] = -1;
    }
    void inisert(int x) {
        int rt = 0;
        for(int i = 30, tmp; i >= 0; --i) {
            tmp = ((x>>i)&1);
            if(nex[rt][tmp] == -1) newnode(), nex[rt][tmp] = tot;
            rt = nex[rt][tmp];
            num[rt] ++;
        }
        val[rt] = x;
    }
    int query(int x) {
        int rt = 0;
        for(int i = 30, tmp; i >= 0; --i) {
            tmp = ((x>>i)&1);
            if(nex[rt][tmp] != -1 && num[nex[rt][tmp]]) rt = nex[rt][tmp];
            else rt = nex[rt][!tmp];
        }
        return val[rt];
    }
    int find() {
        int rt = 0;
        for(int i = 30, tmp; i >= 0; --i) {
            if(nex[rt][0] != -1 && num[nex[rt][0]]) rt = nex[rt][0];
            else if(nex[rt][1] != -1 && num[nex[rt][1]]) rt = nex[rt][1];
        }
        if(rt == 0) return -1;
        return val[rt];
    }
    void del() {
        for(int i = 0; i <= tot + 1; ++i) num[i] = 0, clr(nex[i], -1);
        tot = 0;
    }
    void sub(int x) {
        int rt = 0;
        for(int i = 30, tmp; i >= 0; --i) {
            tmp = ((x>>i)&1);
            rt = nex[rt][tmp];
            num[rt] --;
        }
    }
}cw[2];
/*
 * 這種做法不能保證每次求出來的異或最小值都是單調遞增的,但是將n次得到的值排序後一定是正確答案
 * 如果想單純的求兩個01字典樹異或最小值,個人感覺還沒有較好的複雜度的做法。
 * 關於本題,還有一個出題人提供適用性更加廣泛的正解:
 * 現在a中隨便找一個數字,然後在b中找一個和他匹配最小的數字,再在a裏面找一個和上個數匹配最小的數字,遞歸下去一定會找到一個大小爲2的環
 * 把這個環取出來,在回到上一個位置繼續遞歸下去。得到的n個數字排序即爲最終答案。
 * */
vector<int> vs;
int dfs(int id, int x, int lst) {
    int tmp = cw[!id].query(x);
    if(tmp == lst) {
        vs.eb(tmp ^ x);
        cw[id].sub(x);
        cw[!id].sub(tmp);
        return id;
    }
    int ret = dfs(!id, tmp, x);
    if(ret != id) return ret;
}
int main() {
#ifndef ONLINE_JUDGE
    freopen("/home/cwolf9/CLionProjects/ccc/in.txt", "r", stdin);
//    freopen("/home/cwolf9/CLionProjects/ccc/out.txt", "w", stdout);
#endif
    int tim = read();
    while(tim --) {
        n = read();
        for(int i = 1; i <= n; ++i) ar[i] = read(), cw[0].inisert(ar[i]);
        for(int i = 1; i <= n; ++i) br[i] = read(), cw[1].inisert(br[i]);
        vs.clear();
        while(1) {
            int tmp = cw[0].find();
            if(tmp == -1) break;
            dfs(1, tmp, -1);
        }
        sort(all(vs));
        for(int i = 0; i < SZ(vs); ++i) printf("%d%c", vs[i], " \n"[i == SZ(vs) - 1]);
        cw[0].del(), cw[1].del();
    }
    return 0;
}

原題描述

在這裏插入圖片描述

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