CF600E Lomsat gelral 【线段树合并】

题目链接

CF600E

题解

容易想到就是线段树合并,维护每个权值区间出现的最大值以及最大值位置之和即可

对于每个节点合并一下两个子节点的信息

要注意叶子节点信息的合并和非叶节点信息的合并是不一样的

由于合并不比逐个插入复杂度高,所以应是\(O(nlogn)\)的

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<map>
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define mp(a,b) make_pair<int,int>(a,b)
#define cls(s) memset(s,0,sizeof(s))
#define cp pair<int,int>
#define LL long long int
using namespace std;
const int maxn = 100005,maxm = 8000005,INF = 1000000000;
inline int read(){
    int out = 0,flag = 1; char c = getchar();
    while (c < 48 || c > 57){if (c == ‘-‘) flag = -1; c = getchar();}
    while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
    return out * flag;
}
LL sum[maxm],ans[maxn];
int ls[maxm],rs[maxm],mx[maxm],cnt;
int n,c[maxn],fa[maxn],rt[maxn];
int h[maxn],ne = 1;
struct EDGE{int to,nxt;}ed[maxn << 1];
inline void build(int u,int v){
    ed[++ne] = (EDGE){v,h[u]}; h[u] = ne;
    ed[++ne] = (EDGE){u,h[v]}; h[v] = ne;
}
inline void upd(int u){
    sum[u] = 0;
    if (mx[ls[u]] >= mx[rs[u]]){
        mx[u] = mx[ls[u]];
        sum[u] += sum[ls[u]];
    }
    if (mx[rs[u]] >= mx[ls[u]]){
        mx[u] = mx[rs[u]];
        sum[u] += sum[rs[u]];
    }
}
void modify(int& u,int pre,int l,int r,int pos){
    u = ++cnt; ls[u] = ls[pre]; rs[u] = rs[pre];
    if (l == r){mx[u] = mx[pre] + 1; sum[u] = l; return;}
    int mid = l + r >> 1;
    if (mid >= pos) modify(ls[u],ls[pre],l,mid,pos);
    else modify(rs[u],rs[pre],mid + 1,r,pos);
    upd(u);
}
int merge(int u,int v,int l,int r){
    if (!u) return v;
    if (!v) return u;
    int t = ++cnt,mid = l + r >> 1;
    if (l == r){
        mx[t] = mx[u] + mx[v];
        sum[t] = l;
        return t;
    }
    ls[t] = merge(ls[u],ls[v],l,mid);
    rs[t] = merge(rs[u],rs[v],mid + 1,r);
    upd(t);
    return t;
}
void dfs(int u){
    modify(rt[u],rt[u],1,n,c[u]);
    Redge(u) if ((to = ed[k].to) != fa[u]){
        fa[to] = u; dfs(to);
        rt[u] = merge(rt[u],rt[to],1,n);
    }
    ans[u] = sum[rt[u]];
}
int main(){
    n = read();
    REP(i,n) c[i] = read();
    for (int i = 1; i < n; i++) build(read(),read());
    dfs(1);
    REP(i,n) printf("%lld ",ans[i]);
    return 0;
}

原文地址:https://www.cnblogs.com/Mychael/p/9210792.html

时间: 2024-10-26 17:11:34

CF600E Lomsat gelral 【线段树合并】的相关文章

CF600E Lomsat gelral(线段树合并)

link 题目大意:给以1为根的一棵树,求树上每个点子树中出现次数最多的权值(如果有多个就求他们的和) 对每个点开一个线段树维护子树内权值的桶,dfs时候线段树合并就行了. 因为最后线段树一共插入最多 \(O(n\log n)\) 个节点,每个节点最多会被合并一次,所以复杂度是 \(O(n\log n)\) 的. #include <cstdio> #include <vector> using namespace std; struct fuck { int maxval; lo

codeforces 600E . Lomsat gelral (线段树合并)

You are given a rooted tree with root in vertex 1. Each vertex is coloured in some colour. Let's call colour c dominating in the subtree of vertex v if there are no other colours that appear in the subtree of vertex v more times than colour c. So it'

CodeForces 600E Lomsat gelral(线段树合并)

题目链接:http://codeforces.com/problemset/problem/600/E You are given a rooted tree with root in vertex 1. Each vertex is coloured in some colour. Let's call colour c dominating in the subtree of vertex v if there are no other colours that appear in the

线段树合并:从入门到放弃

感谢这篇博客(这里跳转)以及邱宇大神的讲解,我也算作入门(自闭)了. 需要掌握的前置知识点:动态开点线段树.权值线段树. 一.合并思想 线段树合并,就是指建立一颗新的线段树,保存原有的两颗线段树的信息. 那么就是: 假设现在合并到了线段树的a.b某一个pos 如果a在这个区间有pos,b没有,那么新的线段树pos位置赋值为a 如果b在这个区间有pos,a没有,那么新的线段树pos位置赋值为b 如果a.b都有,那么继续 如果此时处理到了两颗线段树的叶子节点,就把b在pos的值加到a上,把新线段树上

【BZOJ4399】魔法少女LJJ 线段树合并

[BZOJ4399]魔法少女LJJ Description 在森林中见过会动的树,在沙漠中见过会动的仙人掌过后,魔法少女LJJ已经觉得自己见过世界上的所有稀奇古怪的事情了LJJ感叹道“这里真是个迷人的绿色世界,空气清新.淡雅,到处散发着醉人的奶浆味:小猴在枝头悠来荡去,好不自在:各式各样的鲜花争相开放,各种树枝的枝头挂满沉甸甸的野果:鸟儿的歌声婉转动听,小河里飘着落下的花瓣真是人间仙境”SHY觉得LJJ还是太naive,一天,SHY带着自己心爱的图找到LJJ,对LJJ说:“既然你已经见识过动态树

【BZOJ2733】永无乡[splay启发式合并or线段树合并]

题目大意:给你一些点,修改是在在两个点之间连一条无向边,查询时求某个点能走到的点中重要度第k大的点.题目中给定的是每个节点的排名,所以实际上是求第k小:题目求的是编号,不是重要度的排名.我一开始差点被这坑了. 网址:http://www.lydsy.com/JudgeOnline/problem.php?id=2733 这道题似乎挺经典的(至少我看许多神犇很早就做了这道题).这道题有两种写法:并查集+(splay启发式合并or线段树合并).我写的是线段树合并,因为--splay不会打+懒得学.

BZOJ 4756 线段树合并(线段树)

思路: 1.最裸的线段树合并 2. 我们可以观察到子树求一个东西 那我们直接DFS序好了 入队的时候统计一下有多少比他大的 出的时候统计一下 减一下 搞定~ 线段树合并代码: //By SiriusRen #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N=100050; int n,col[N],cpy[N],tree[N*100],lso

bzoj2733: [HNOI2012]永无乡(splay+启发式合并/线段树合并)

这题之前写过线段树合并,今天复习Splay的时候想起这题,打算写一次Splay+启发式合并. 好爽!!! 写了长长的代码(其实也不长),只凭着下午的一点记忆(没背板子...),调了好久好久,过了样例,submit,1A! 哇真的舒服 调试输出懒得删了QwQ #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<queue> #include

[BZOJ3545] [ONTAK2010]Peaks(线段树合并 + 离散化)

传送门 由于困难值小于等于x这个很恶心,可以离线处理,将边权,和询问时的x排序. 每到一个询问的时候,将边权小于等于x的都合并起来再询问. .. 有重复元素的线段树合并的时间复杂度是nlog^2n #include <cstdio> #include <iostream> #include <algorithm> #define N 500001 int n, m, q, cnt, tot, size; int sum[N * 10], ls[N * 10], rs[N