1. 程式人生 > 實用技巧 >Xor-MST學習/ 2020牛客暑假多校(五)B.Graph

Xor-MST學習/ 2020牛客暑假多校(五)B.Graph

Xor-MST學習/ 2020牛客暑假多校(五)B.Graph

補題連結

題目大意:

給一個完全圖,求異或最小生成樹。

題解:

首先先看一下這道題:CF888G 。與本題不同之處是這兩個題一個給點的權值,一個給邊的權值。但實際上一樣可以相互轉換。如讓本題中a[1]= 0,跑一下dfs,算出所有點的權值就變為CF888題了。

處理異或,考慮使用Trie樹。Trie樹除了可以處理字串外,還可以處理二進位制。把點值插入Trie樹,會發現,兩個葉子節點的LCA越深,則它們代表的值的異或值越小(LCA相等時情況不一定)。 要使異或值最小就要儘量讓高位相等,儘量走同一條路徑。

所以我們計算每一個子樹的貢獻,考慮啟發式合併,每次做siz小的一邊在字典樹上取計算siz大的一邊的貢獻。複雜度O(nlogn)。總複雜度\(O(nlog^{2}n)\)

其實把CF888G那題當作一個Xor-MST模板題學會,這一題就直接是套路題了

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<map>
#include<queue>
#include<vector>
#include<string>
#include<fstream>
using namespace std;
#define rep(i, a, n) for(int i = a; i <= n; ++ i)
#define per(i, a, n) for(int i = n; i >= a; -- i)
typedef long long ll;
const int N = 5e6 + 105;
const int mod = 998244353;
const double Pi = acos(- 1.0);
const int INF = 0x3f3f3f3f;
const int G = 3, Gi = 332748118;
ll qpow(ll a, ll b) { ll res = 1; while(b){ if(b & 1) res = (res * a) % mod; a = (a * a) % mod; b >>= 1;} return res; }
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
//
int n;
ll a[N];
int head[N], cnt = 0;
struct node{
    int to, nxt, c;
}edge[N];

void add(int u, int v, int w){
    edge[cnt].to = v, edge[cnt].c = w, edge[cnt].nxt = head[u], head[u] = cnt ++;
    edge[cnt].to = u, edge[cnt].c = w, edge[cnt].nxt = head[v], head[v] = cnt ++;
}

struct XorTrie{
    int cnt;
    int t[N][2];
    int L[N], R[N];
    void _init(){
        cnt = 0;
    }
    void Insert(ll x, int id){
        int rt = 0;
        for(int i = 32; i >= 0; -- i){
            int op = x >> i & 1ll;
            if(!t[rt][op]) t[rt][op] = ++ cnt;
            rt = t[rt][op];
            
            if(!L[rt]) L[rt] = id;
            R[rt] = id;
        }
    }
    ll ask(int rt, int pos, ll x){
        ll res = 0;
        for(int i = pos; i >= 0; -- i){
            int op = x >> i & 1ll;
            if(t[rt][op]) rt = t[rt][op];
            else {
                rt = t[rt][!op];
                res += (1ll << i);
            }
        }
        return res;
    }
    
    ll query(int rt, int pos){
        if(t[rt][0] && t[rt][1]){
            int x = t[rt][0], y = t[rt][1];
            ll minn = 1e18;
            for(int i = L[x]; i <= R[x]; ++ i) 
                minn = min(minn, ask(y, pos - 1, a[i]) + (1ll << pos));
            return minn + query(t[rt][0], pos - 1) + query(t[rt][1], pos - 1);
        }
        else if(t[rt][0]) return query(t[rt][0], pos - 1);
        else if(t[rt][1]) return query(t[rt][1], pos - 1);
        return 0;
    }
    
}trie;


void dfs(int u, int pre){
    for(int i = head[u] ; i != -1; i = edge[i].nxt){
        int v = edge[i].to, w = edge[i].c;
        if(v == pre) continue;
        a[v] = a[u] ^ w;
        dfs(v, u);
    }
}

int main()
{
    memset(head, -1, sizeof(head));
    trie._init();
    scanf("%d",&n);
    for(int i = 1; i < n; ++ i){
        int x, y, z; scanf("%d%d%d",&x,&y,&z);
        x ++; y ++;
        add(x, y, z);
    }
    a[1] = 0;
    dfs(1, -1);
    sort(a + 1, a + n + 1);
    for(int i = 1; i <= n; ++ i) trie.Insert(a[i], i);
    printf("%lld\n",trie.query(0, 32));
    return 0;
}