Loading

【题解】P6072 『MdOI R1』Path

思路

可以用启发式合并 Trie 啦!

首先考虑到对于合法的方案,必然存在一个结点 \(x\) 使得其中一条路径在 \(x\) 的子树中而另一条不在,实际上 \(x\) 是两条路径中最浅点之一啦!

所以只需要考虑维护出每个子树内的最优路径和子树外的就好啦!

注意到路径异或和等价于端点的树上前缀异或和的异或和,不妨先处理出来好啦!

子树内的可以直接启发式合并 01 Trie 做啦!

子树外的可以考虑一条最优路径 \(p \rightarrow q\),不以这条路径为答案的点只存在于根到 \(p, q\) 的路径上,另外处理就好啦!

实际上按顺序遍历一遍,把父亲结点除当前结点之外的子树都加入 Trie 就行啦!

时间复杂度是 \(O(n \log n \log V)\) 啦!

绫绫 AI 好耶!!!1 冲冲冲冲冲

代码

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

const int maxn = 3e4 + 5;
const int t_sz = maxn * 100;

int n, p, q;
int tot, lg_max;
int a[maxn], fa[maxn], tag[maxn], bel[maxn];
int rt[maxn], in[maxn], out[maxn], rk[maxn];
int cnt[t_sz], son[t_sz][2];
vector<int> g[maxn], w[maxn], v[maxn];

int lg2(int x) { return (x == 0 ? -1 : lg2(x >> 1) + 1); }

void insert(int &t, int v)
{
    if (!t) t = ++tot;
    cnt[t]++;
    for (int i = lg_max, x = t; ~i; i--)
    {
        bool c = v >> i & 1;
        if (!son[x][c]) son[x][c] = ++tot;
        x = son[x][c], cnt[x]++;
    }
}

void clear() { memset(cnt + 1, 0, (tot + 1) * sizeof(int)); }

int query(int t, int v)
{
    int res = 0;
    for (int i = lg_max, x = t; ~i; i--)
    {
        bool c = ~v >> i & 1;
        if (cnt[son[x][c]]) x = son[x][c], res |= (1ll << i);
        else x = son[x][c ^ 1];
    }
    return res;
}

void merge(int t, int p, int cur, int dep, int &v)
{
    if (!t) return;
    if (!~dep)
    {
        v = max(v, query(rt[p], cur));
        insert(rt[p], cur);
        return;
    }
    merge(son[t][0], p, cur, dep - 1, v);
    merge(son[t][1], p, cur | (1 << dep), dep - 1, v);
}

void dfs1(int u)
{
    static int now = 0;
    rk[++now] = u;
    in[u] = 0, insert(rt[u], a[u]);
    for (int i = 0; i < g[u].size(); i++)
    {
        int v = g[u][i], d = w[u][i];
        if (v == fa[u]) continue;
        fa[v] = u, a[v] = a[u] ^ d;
        dfs1(v);
        if (cnt[rt[u]] < cnt[rt[v]]) swap(rt[u], rt[v]);
        merge(rt[v], u, 0, lg_max, in[u]);
        in[u] = max(in[u], in[v]);
    }
}

void dfs2(int u)
{
    clear();
    for (int x = u; x; x = fa[x]) tag[x] = 1;
    int cur = 0;
    for (int k = 1, i; k <= n; v[i].clear(), v[bel[i]].push_back(a[i]), k++)
        if (tag[i = rk[k]]) bel[i] = i;
        else bel[i] = bel[fa[i]];
    for (int k = 1, i; k <= n; k++)
        if (tag[i = rk[k]])
        {
            for (int w : v[fa[i]]) cur = max(cur, query(rt[0], w)), insert(rt[0], w);
            out[i] = max(out[i], cur), tag[i] = 0;
        }
}

int main()
{
    scanf("%d", &n);
    for (int i = 1, u, v, d; i <= n - 1; i++)
    {
        scanf("%d%d%d", &u, &v, &d);
        lg_max = max(lg_max, lg2(d));
        g[u].push_back(v), w[u].push_back(d);
        g[v].push_back(u), w[v].push_back(d);
    }
    dfs1(1);
    clear();
    int va = 0, vb = 0;
    for (int i = 1; i <= n; i++)
    {
        int val = query(rt[0], a[i]);
        insert(rt[0], a[i]);
        out[i] = -1;
        if ((va ^ vb) < val) va = a[i], vb = val ^ va, p = i;
    }
    for (int i = 1; i <= n; i++)
        if (a[i] == vb) q = i;
    dfs2(p), dfs2(q);
    for (int i = 1; i <= n; i++)
        if (!~out[i]) out[i] = va ^ vb;
    int ans = 0;
    // printf("debug %d %d\n", p, q);
    // for (int i = 2; i <= n; i++) printf("%d : %d %d\n", i, in[i], out[i]);
    for (int i = 2; i <= n; i++) ans = max(ans, in[i] + out[i]);
    printf("%d\n", ans);
    return 0;
}

/*
2 : 11 15
3 : 7 11
4 : 3 15
5 : 0 15
6 : 0 15
7 : 7 14
8 : 0 15
9 : 0 14

2 : 11 7
3 : 7 11
4 : 3 7
5 : 0 15
6 : 0 15
7 : 7 14
8 : 0 15
9 : 0 14
*/
posted @ 2023-01-13 18:39  kymru  阅读(20)  评论(0编辑  收藏  举报