周赛十四 D 函数最大值

D 函数最大值

原题:poj 3764

问题描述:
给定一颗带边权的树,定义 f(u,v) 为 u 到 v 的唯一路径上的所有边权的异或,特
别地,当 u=v 时 f(u,v)=0,求 f(u,v) 的最大值,其中 u 和 v 是给定树上的
两个结点。
输入描述:
第一行为一个整数 N,表示树上的结点数
接下来 N-1 行,给出 u,v,w 表示树上的 u 点和 v 点之间有一条边权为 w 的边。
1<=N<=1e5;
0<=u,v 0<=w<=2^31.
输出描述:
输出一行,一个整数,表示 f(u,v) 的最大值.
示例:
输入

6
0 1 1
1 2 4
1 3 5
0 4 2
4 5 1

输出

7

思路:利用a ^ b ^ b = a的特性.
建立一个数组d[i], 储存i到0的异或值.
u->v的异或值就等于d[u]^d[v].(根节点到u,v最近公共祖节点的异或值抵消了)

     1
     | (2)
     2
 (4)/ \ (1)
   3   4

f(3,4)= d[3] ^ d[4] = 2 ^ 4 ^ 2 ^ 1 = 4 ^ 1
这里可以看到0->3 和 0->4 的公共路径部分抵消了.
d[i]的异或值,通过链式前向星dfs()解决.
最后变成从数组d中寻找两个d[i],d[j]异或最大值

直接双重for循环暴力,肯定是不行的.
这里就要用到01字典树的知识了.(通过注释解释)
遵循高位优先原则建树,这样异或才最大.

#include 
#include 
#include 
using namespace std;
const int N = 1e5 + 5;
struct Edge {
    int to;
    int next;
    int val;
} e[N << 1];
int tot;
int head[N];
void add(int u, int v, int val) {
    e[++tot].to = v;
    e[tot].next = head[u];
    e[tot].val = val;
    head[u] = tot;
}
int n;
int d[N];
//dfs,链式前向星,用d[i]记录0->i的异或值
//d[0]=0
void dfs(int u, int fa) {
    for (int i = head[u]; i; i = e[i].next) {
        int v = e[i].to;
        if (v == fa) {
            continue;
        }
        d[v] = (d[u] ^ e[i].val);
        dfs(v, u);
    }
}
int cnt = 0;
int trie[N * 30][2];
//trie[当前节点][当前对应位数]=下个节点
//用数组元素是否为0,判断是否含有这位数
void build(int x) {
    int tmp;
    int p = 0;
    for (int i = 30; i >= 0; i--) {
        tmp = (x >> i) & 1;//从高为开始
        //不存在就新建节点
        if (!trie[p][tmp]) {
            trie[p][tmp] = ++cnt;//下节点
            p = cnt;
        } else {
        	//存在节点了,就不用在建立,直接下一个节点
            p = trie[p][tmp];
        }
    }
}
int query(int x) {
    int res = 0, p = 0, tmp, tmp1;
    //从最高位开始
    for (int i = 30; i >= 0; i--) {
        tmp = (x >> i) & 1;
        tmp1 = tmp ^ 1;//位数相反,0->1,1->0
        if (trie[p][tmp1]) {
            res += 1 << i;
            p = trie[p][tmp1];
        } else {
        	//这个下节点一定存在
        	//只有0或者1,一方没有,另外一方肯定存在
            p = trie[p][tmp];
        }
    }
    return res;
}
int main() {
    while (~scanf("%d", &n)) {
        memset(head, 0, sizeof(head));
        memset(trie, 0, sizeof(trie));
        tot = cnt = 0;
        int u, v, val;
        for (int i = 0; i < n - 1; i++) {
            scanf("%d%d%d", &u, &v, &val);
            add(u, v, val);
            add(v, u, val);
        }
        dfs(0, -1);
        for (int i = 0; i < n; i++) {
            build(d[i]);
        }
        int ans = 0;
        for (int i = 0; i < n; i++) {
            ans = max(ans, query(d[i]));
        }
        printf("%d\n", ans);
    }
    return 0;
}

你可能感兴趣的:(周赛十四 D 函数最大值)