zoukankan      html  css  js  c++  java
  • 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

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

  • 相关阅读:
    flask读书笔记-flask web开发
    flask 杂记
    ubuntu redis 安装 &基本命令
    redis 订阅&发布(转载)
    Redis键值设计(转载)
    python2 python3区别
    捕获异常
    开源的微信个人号接口
    工具
    HDU2966 In case of failure(浅谈k-d tree)
  • 原文地址:https://www.cnblogs.com/zgglj-com/p/9738597.html
Copyright © 2011-2022 走看看