Love Live!

题意

一颗(n(2le n le10^5))个结点的树,树上的每条边有一个边权(w(1le w < n)),且所有边的边权是互不相同的。定义一条简单路径的(beautiful)为该路径上所有边的异或和,定义一条简单路径的(diffculty)为该路径上所有边的边权最大值。求对于([1,n))的每种(diffculty),所对应的最大(beautiful)

题解

假设根节点为1号结点,一条简单路径(u -u_1-u_2-u_3-dots -v),记该简单路径上所有边权的异或和为(sum_{(u,v)}),那么(sum_{(u,v)} = sum_{(u,1)}igoplus sum_{(v,1)})因为简单路径的困难度是该路径上的最大边权,所以先将树边按照从小到大排序,然后在加边的同时用并查集维护联通块。再对每个连通块开一个(01-tire)树以求解“对于给定的(x),在集合中找一个数字(y),使得(xigoplus y)最大的问题”。最后每加一条边,就启发式合并这条边连接的两个连通块并记录答案。(O(nlog^2n))

收获

启发式合并(树的合并和剖分)

简单路径的异或和可以拆分成两端点到根结点的异或和的异或和

代码

const int N = 100005;

struct Edge {
    int u, v, next, w;
    Edge() {}
    Edge(int u, int v, int w) : u(u), v(v), w(w) {}
    Edge(int u, int v, int w, int next) : u(u), v(v), w(w), next(next) {}

    bool operator < (const Edge &r) const {
        return w < r.w;
    }
}e[N << 1], G[N];

struct Tire {
    int tot;
    int T[N], a[N * 200][2];

    void Inite(int cnt) {
        tot = cnt + 1;
        mem(a, 0);
    }
    void Insert(int root, int x) {
        bitset<20> b(x);
        for (int i = 19; ~i; --i) {
            if (!a[root][b[i]]) a[root][b[i]] = ++tot;
            root = a[root][b[i]];
        }
    }
    int Query(int root, int x) {
        bitset<20> b(x);
        int res = 0;
        for (int i = 19; ~i; --i) {
            int id = b[i] ^ 1;
            bool flag = true;
            if (!a[root][id]) {
                flag = false;
                id = id ^ 1;
            }
            if (flag) res += (1 << i);
            root = a[root][id];
        }
        return res;
    }
}tire;

int n, tot;
int head[N], prefix[N], pa[N], sz[N];

vector<int> v[N];

void Inite() {
    tire.Inite(n);
    mem(head, -1);
    prefix[1] = tot = 0;
}

void addedge(int u, int v, int w) {
    e[++tot] = Edge(u, v, w, head[u]);
    head[u] = tot;
}

void DFS(int u, int p) {
    for (int i = head[u]; ~i; i = e[i].next) if (e[i].v != p) {
        int v = e[i].v;
        prefix[v] = prefix[u] ^ e[i].w;
        DFS(v, u);
    }
}

int Find(int a) {
    return (a == pa[a] ? a : pa[a] = Find(pa[a]));
}

int main()
{
    sc(n);
    Inite();

    rep(i, 1, n) {
        int u, v, w;
        sc(u), sc(v), sc(w);
        addedge(u, v, w);
        addedge(v, u, w);
        G[i] = Edge(u, v, w);
    }

    DFS(1, 1);

    Rep(i, 1, n) {
        pa[i] = i;
        sz[i] = 1;

        tire.T[i] = i;
        tire.Insert(i, prefix[i]);

        v[i].pb(prefix[i]);
    }

    sort(G + 1, G + n);

    rep(i, 1, n) {
        int pu = Find(G[i].u), pv = Find(G[i].v);

        if (sz[pu] > sz[pv]) swap(pu, pv);

        int ans = 0;
        for (auto it : v[pu]) ans = max(ans, tire.Query(tire.T[pv], it));

        for (auto it : v[pu]) {
            tire.Insert(tire.T[pv], it);
            v[pv].pb(it);
        }

        pa[pu] = pv;
        sz[pv] = sz[pv] + sz[pu];

        printf("%d%c", ans, (i + 1 == n ? '
' : ' '));
    }

    return 0;
}

ps

还是有几处不明白,启发式合并的复杂度是怎么算的,这棵树的结点该怎么估算。

原文地址:https://www.cnblogs.com/zgglj-com/p/9738597.html