首页 > 其他分享 >E. Lomsat gelral

E. Lomsat gelral

时间:2024-05-11 11:33:03浏览次数:20  
标签:cnt SegmentTreeNode int res sum st gelral Lomsat

https://codeforces.com/contest/600/problem/E

题意:给一颗树,如果当前叶子为根的树中数字出现最多次数为k,求该树中所有出现次数为k的数字之和。

思路:dfs + 线段树合并。

总结:第一次接触线段树合并,整理了3个上午才整理出模板来,不知道这种线段树合并有没有区间更新的功能,这个题目是单点更新,所以板子就先写成单点的了。

/*
  线段树的节点
  重载了合并区间符号 '+'
  实现了区间上更新数值的模板函数
*/
class SegmentTreeNode {
public:
    SegmentTreeNode() {}

    /*
      重载左右孩子区间合并操作,要根据题目自己写
    */
    friend SegmentTreeNode operator + (const SegmentTreeNode& a, const SegmentTreeNode& b) {
        SegmentTreeNode res{ a };
        if (a.cnt == b.cnt) {
            res.sum = a.sum + b.sum;
        }
        else if (a.cnt < b.cnt) {
            res.cnt = b.cnt;
            res.sum = b.sum;
        }
        return res;
    }

    /*
      重载线段树叶子节点合并,要根据题意自己写
    */
    friend SegmentTreeNode operator | (const SegmentTreeNode& a, const SegmentTreeNode& b){
        SegmentTreeNode res = a;
        res.cnt += b.cnt;
        res.sum = max(res.sum, b.sum);
        return res;
    }


    /*
      区间更新值value操作,在当前的区间节点上更新数值value到该区间
    */
    template<typename T>
    void applyUpdate(T value) {
        cnt += 1;
        sum = value;
    }
    int cnt = 0;
    long long sum = 0;

};




/*
  多重线段树,用于线段树合并类操作
  该类实例化需要传参,参数是要更新的区间类型,可以为基础数据类型或者自己实现的类型,该类型与节点中的模板函数参数类型对应
  该方法完全动态分配空间(类似字典树内存管理),在时间与性能上都有着不俗的优势
*/
template<typename T>
class MultiSegmentTree {
public:
    MultiSegmentTree(int sz) : sz_(sz) {
        st_.resize(1);
        lchild_.resize(1);
        rchild_.resize(1);
        root_.resize(sz_);
    }

    /*
      更新线tree_id线段树
    */
    void update(int tree_id, int pos, T value) {
        checkNodeIndex(root_[tree_id]);
        update(root_[tree_id], 1, sz_, pos, value);
    }

    /*
      合并两棵线段树
    */
    void merge(int u, int v) {
        assert(u && v && std::max(u, v) < sz_);
        root_[u] = merge(root_[u], root_[v], 1, sz_);
    }

    /*
      需要根据问题重新手写,获取当前线段树的结果
    */
    long long get(int u) {
        assert(u < sz_);
        return st_[root_[u]].sum;
    }


private:
    int sz_;
    std::vector<SegmentTreeNode> st_;
    std::vector<int> lchild_;
    std::vector<int> rchild_;
    std::vector<int> root_;

    /*
      线段树单点更新,位置在pos,数值是value
    */
    void update(int p, int l, int r, int pos, T value) {
        if (l == r) {
            st_[p].applyUpdate(pos);
            return;
        }
        int mid = (l + r) >> 1;
        if (pos <= mid) {
            checkNodeIndex(lchild_[p]);
            update(lchild_[p], l, mid, pos, value);
        }
        else {
            checkNodeIndex(rchild_[p]);
            update(rchild_[p], mid + 1, r, pos, value);
        }

        st_[p] = st_[lchild_[p]] + st_[rchild_[p]];
    }

    /*
      合并两颗线段树
    */
    int merge(int p, int q, int l, int r) {
        if (!p || !q) {
            return p + q;
        }
        if (l == r) {
            st_[p] = st_[p] | st_[q];
            return p;
        }
        int mid = (l + r) >> 1;
        lchild_[p] = merge(lchild_[p], lchild_[q], l, mid);
        rchild_[p] = merge(rchild_[p], rchild_[q], mid + 1, r);
        st_[p] = st_[lchild_[p]] + st_[rchild_[p]];
        return p;
    }

    /*
      使用该函数完全动态分配空间,一般空间比直接分配内存节省百分之50
    */
    inline void checkNodeIndex(int& index) {
        if (index == 0) {
            index = st_.size();
            st_.emplace_back();
            lchild_.emplace_back(0);
            rchild_.emplace_back(0);
        }
    }
};



void preProcess(){

}








void solve(){
    int n;
    cin >> n;

    vector<int> a(n + 1);
    for (int i = 1; i <= n; ++i){
        cin >> a[i];
    }

    vector<vector<int>>al(n + 1);
    for (int i = 0; i < n - 1; ++i){
        int u, v;
        cin >> u >> v;
        al[u].push_back(v);
        al[v].push_back(u);
    }

    MultiSegmentTree<int> st(n + 1);
    vector<long long> ans(n + 1);

    function<void(int, int)> dfs = [&](int u, int p){
        for (const auto& v : al[u]){
            if (v != p){
                dfs(v, u);
                st.merge(u, v);
            }
        }
        st.update(u, a[u], 1);
        ans[u] = st.get(u);
    };

    dfs(1, 0);

    for (int i = 1; i <= n; ++i){
        cout << ans[i] << " \n"[i == n];
    }
}

标签:cnt,SegmentTreeNode,int,res,sum,st,gelral,Lomsat
From: https://www.cnblogs.com/yxcblogs/p/18186162

相关文章

  • CF600E Lomsat gelral
    题意给定一棵根为\(1\)的有根树。每个节点有颜色,求每个节点子树内出现最多的颜色编号之和。SolDsuontree板子题。首先对于整棵树进行轻重链剖分,注意到一个关键性质:轻边只有\(log\)条。\(n^2\)的暴力是\(trivial\)的,不再赘述。注意到中间有很多节点被重复计算了......
  • CF600E Lomsat gelral
    树上启发式合并(dsuontree)通常用来查询不带修的子树信息,信息要求可合并。对于一个结点\(u\),其步骤如下:求解其轻儿子的答案,同时清除递归产生的影响。求解其重儿子的答案,保留递归产生的影响。将轻儿子子树内的每个结点都合并进答案中,同时成为以\(u\)为根的子树产生的影响。......
  • CF600E Lomsat gelral(树上启发式合并)
    题目链接:https://codeforces.com/problemset/problem/600/E这是一道树上启发式合并的题,就只是在模板的基础上稍微变化了一下解题思路:我们需要计算当前u节点的答案,要计算加入非重链节点对此答案的影响,在计算加入节点对ans影响的时候,遍历u除了重链外的所有子树节点(因为重链部分的......
  • Educational Codeforces Round 2 E Lomsat gelral 线段树合并
    题目链接大致题意给你一棵有n个点的树,树上每个节点都有一种颜色ci(ci≤n),让你求每个点子树出现最多的颜色/编号的和记性不好,主要是为了防止自己忘掉,今天和队友合作写题......
  • Lomsat gelral
    Lomsatgelral统计节点出现次数最多的颜色之和概要:运用树链剖分,每次不用重新跑重儿子,从而将复杂度降低到nlogn#include<bits/stdc++.h>usingnamespacestd;usingl......