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

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

  • 相关阅读:
    算法----(1)冒泡排序
    淘宝爬虫
    爬虫_豆瓣电影top250 (正则表达式)
    爬虫_猫眼电影top100(正则表达式)
    Android 简单调用摄像头
    Android 简单天气预报
    思维模型
    This view is not constrained, it only has designtime positions, so it will jump to (0,0) unless you
    Android studio preview界面无法预览,报错render problem
    Android studio 3.1.2报错,no target device found
  • 原文地址:https://www.cnblogs.com/zgglj-com/p/9738597.html
Copyright © 2011-2022 走看看