CF1625D (01trie应用)

Binary Spiders

选出最大的防御蜘蛛数量,最少要有两只,并且任意两只防御蜘蛛都的XOR值大于等于k。

  • 令m为k的最高位1的位。则若存在两个数在m位以上的位不同,则一定大于k。
  • 若m以上的位相同,则在相同的大于m位的前缀下能否找出两个数比k大,这样的数是只有两个的,因为第m位的XOR一定要是1,若am ^ bm = 1, am ^ cm = 1, bm ^ cm = 0。这一组内选一个数和其他组组也可以。
  • 每个数按大于m位的二进制数分组,同组选出来两个xor能大于k的,没有就只选一个。字典树解决选两数得到最大的xor
#include <bits/stdc++.h>
using namespace std;

const int N = 3e5 + 5;

int a[N];
map<int, int> id;
map<int, vector<int>> mp;
int trie[N * 30][2], tot = 1;
void insert(int x) {
   int p = 1;
   for(int i = 30; i >= 0; -- i) {
       bool k = x >> i & 1;
       if(!trie[p][k]) trie[p][k] = ++ tot;
       p = trie[p][k]; 
   }
}
int query(int x) {
   int p = 1; int res = 0;
   for(int i = 30 ; i >= 0; -- i) {
       bool u = x >> i & 1;
       if(trie[p][!u]) res = res << 1 | (!u), p = trie[p][!u];
       else res = res << 1 | u, p = trie[p][u];
   }
   return res;
}
int get_bit(int x) {
   int res = 0;
   while (x){
       ++ res;
       x >>= 1;
   }
   return res;
}
void init() {
   memset(trie, 0, sizeof (int) * 2 * (tot + 1)); tot = 1;
}
int main () {
   int n, k; cin >> n >> k;
   for(int i = 1; i <= n; ++ i) cin >> a[i], id[a[i]] = i;
   int m = get_bit(k);
   if(!k) {
       cout << n << endl;
       for(int i = 1; i <= n; i ++) cout << i << " ";
       return 0;
   }
   for(int i = 1; i <= n; ++ i) {
       mp[a[i] >> m].push_back(a[i]);
   }
   vector<int> ans;
   for(auto i : mp) {
       init();
       for(auto j : i.second) {
           insert(j);
       }
       bool ifin = 0;
       for(auto j : i.second) {

           int qx = query(j);
           if((qx ^ j) >= k) {
               ans.push_back(j); ans.push_back(qx); ifin = 1;
               break;
           }
       }  
       if(!ifin && i.second.size()) ans.push_back(i.second[0]);
   }
   if(ans.size() <= 1) {
       cout << -1 << endl;
   } else {
       cout << ans.size() << endl;
       for(auto i : ans) {
           cout << id[i] << " ";
       }
       cout << endl;
   }
   return 0;
}
 

最大异或路径

一棵树(1e5个点)中的异或路径是一条路径上的所有边权异或。

所出来根节点到每个点的dist[i],答案就是 选两个dist[i]xor起来得到的最大值。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;

const int N = 1e5 + 5;
int h[N], w[N << 1], e[N << 1], ne[N << 1], idx;
int dist[N]; int n;
int trie[N * 32][2], tot = 1;
void add(int a, int b, int c) {
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
}
void init() {
    for(int i = 0; i <= n; ++ i) h[i] = -1, dist[i] = 0; idx = 0;
    memset(trie, 0, sizeof (int) * 2 * (tot + 1)); tot = 1;
}
void dfs(int u, int fa) {
    for(int i = h[u]; ~i; i = ne[i]) {
        int v = e[i]; if(v == fa) continue;
        dist[v] = dist[u] ^ w[i];
        dfs(v, u);
    }
}
void insert(int x) {
    int p = 1;
    for(int i = 30; i >= 0; -- i) {
        bool k = x >> i & 1;
        if(!trie[p][k]) trie[p][k] = ++ tot;
        p = trie[p][k]; 
    }
}
int query(int x) {
    int p = 1; int res = 0;
    for(int i = 30 ; i >= 0; -- i) {
        bool u = x >> i & 1;
        if(trie[p][!u]) res = res << 1 | (!u), p = trie[p][!u];
        else res = res << 1 | u, p = trie[p][u];
    }
    return res;
}
int main() {

    memset(h, -1, sizeof h);
    while (~scanf("%d", &n)){
        for(int i = 1; i < n; ++ i) {
            int u, v, c; scanf("%d %d %d", &u, &v, &c);
            ++ u, ++ v;
            add(u, v, c); add(v, u, c);
        }
        dist[1] = 0;
        dfs(1, -1);

        int ans = 0;
        for(int i = 1; i <= n; ++ i) {
            insert(dist[i]);
        }

        for(int i = 1; i <= n; ++ i) {
            ans = max(ans, query(dist[i]) ^ dist[i]);
        }
        printf("%d\n", ans);
        init();

    }
    
    return 0;
}
posted @ 2022-03-14 09:49  qingyanng  阅读(46)  评论(0编辑  收藏  举报