题目

线段树合并的板子题目了,写一写对线段树合并的理解

首先线段树合并就是把一大堆权值线段树合并起来的算法

尽管复杂度看起来并不是非常科学,但是确是非常优秀的\(O(nlogn)\)

主要的写法两种

int merge(int a,int b,int x,int y) {
if(!a) return b;if(!b) return a;
if(x==y) {d[a]+=d[b];t[a]=x;return a;}
int mid=x+y>>1;
l[a]=merge(l[a],l[b],x,mid),r[a]=merge(r[a],r[b],mid+1,y);
pushup(a);return a;
}

把\(b\)合并到\(a\)上

但是我们这样直接把\(b\)合并过来的话,在以后继续合并\(a\)的时候可能合并过程中就会破坏\(b\)的结构,所以这种方法只适合于离线下来,合并完成之后立刻询问

我们也可以像主席树那样,合并不在原来的树上而是新开节点,这样就不需要离线了,一边询问一边用

int merge(int a,int b,int x,int y) {
if(!a) return b;if(!b) return a;
int root=++cnt;
if(x==y) {d[root]=d[a]+d[b];t[root]=x;return root;}
int mid=x+y>>1;
l[root]=merge(l[a],l[b],x,mid),r[root]=merge(r[a],r[b],mid+1,y);
pushup(root);return root;
}

缺点就是非常炸空间

这道题非常简单,我们直接对每一个节点维护一棵权值线段树,之后我们把一个询问变成一次树上差分的形式,之后直接向上合并线段树就好了

由于空间卡得紧这里采用了第一种方式离线询问

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define maxn 100005
#define M 6000005
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read()
{
char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();
return x;
}
struct E{int v,nxt;}e[maxn<<1];
int l[M],r[M],d[M],t[M];
int top[maxn],fa[maxn],head[maxn],deep[maxn],son[maxn],sum[maxn],X[maxn],Y[maxn],Z[maxn],Ans[maxn];
int n,m,rt[maxn],cnt,R,num;
inline void add(int x,int y) {e[++num].v=y;e[num].nxt=head[x];head[x]=num;}
void dfs1(int x) {
sum[x]=1;int maxx=-1;
for(re int i=head[x];i;i=e[i].nxt)
if(!deep[e[i].v])
{
deep[e[i].v]=deep[x]+1,fa[e[i].v]=x;
dfs1(e[i].v);
sum[x]+=sum[e[i].v];
if(sum[e[i].v]>maxx) maxx=sum[e[i].v],son[x]=e[i].v;
}
}
void dfs2(int x,int topf) {
top[x]=topf;
if(!son[x]) return;
dfs2(son[x],topf);
for(re int i=head[x];i;i=e[i].nxt) if(!top[e[i].v]) dfs2(e[i].v,e[i].v);
}
inline int LCA(int x,int y) {
while(top[x]!=top[y])
{
if(deep[top[x]]<deep[top[y]]) std::swap(x,y);
x=fa[top[x]];
}
if(deep[x]<deep[y]) return x;return y;
}
inline void pushup(int a) {
if(d[l[a]]>=d[r[a]]) d[a]=d[l[a]],t[a]=t[l[a]];
else d[a]=d[r[a]],t[a]=t[r[a]];
}
int change(int a,int x,int y,int pos,int val) {
if(!a) a=++cnt;
if(x==y) {d[a]+=val,t[a]=x;return a;}
int mid=x+y>>1;
if(pos<=mid) l[a]=change(l[a],x,mid,pos,val);
else r[a]=change(r[a],mid+1,y,pos,val);
pushup(a);
return a;
}
int merge(int a,int b,int x,int y) {
if(!a) return b;if(!b) return a;
if(x==y) {d[a]+=d[b];t[a]=x;return a;}
int mid=x+y>>1;
l[a]=merge(l[a],l[b],x,mid),r[a]=merge(r[a],r[b],mid+1,y);
pushup(a);return a;
}
void Redfs(int x) {
for(re int i=head[x];i;i=e[i].nxt)
if(deep[e[i].v]>deep[x]) Redfs(e[i].v),rt[x]=merge(rt[x],rt[e[i].v],1,R);
if(d[rt[x]]) Ans[x]=t[rt[x]];
}
int main()
{
n=read(),m=read();int x,y,z;
for(re int i=1;i<n;i++) x=read(),y=read(),add(y,x),add(x,y);
deep[1]=1,dfs1(1),dfs2(1,1);
for(re int i=1;i<=m;i++) X[i]=read(),Y[i]=read(),Z[i]=read(),R=max(R,Z[i]);
for(re int i=1;i<=m;i++)
{
int lca=LCA(X[i],Y[i]);
rt[X[i]]=change(rt[X[i]],1,R,Z[i],1),rt[Y[i]]=change(rt[Y[i]],1,R,Z[i],1);
rt[lca]=change(rt[lca],1,R,Z[i],-1);
if(fa[lca]) rt[fa[lca]]=change(rt[fa[lca]],1,R,Z[i],-1);
}
Redfs(1);
for(re int i=1;i<=n;i++) printf("%d\n",Ans[i]);
return 0;
}

luogu4566 [Vani有约会]雨天的尾巴的更多相关文章

  1. [Vani有约会]雨天的尾巴 线段树合并

    [Vani有约会]雨天的尾巴 LG传送门 线段树合并入门好题. 先别急着上线段树合并,考虑一下这题的暴力.一看就是树上差分,对于每一个节点统计每种救济粮的数量,再一遍dfs把差分的结果统计成答案.如果 ...

  2. 洛谷 P4556 [Vani有约会]雨天的尾巴 解题报告

    P4556 [Vani有约会]雨天的尾巴 题目背景 深绘里一直很讨厌雨天. 灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切. 虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒 ...

  3. P4556 [Vani有约会]雨天的尾巴(线段树合并+lca)

    P4556 [Vani有约会]雨天的尾巴 每个操作拆成4个进行树上差分,动态开点线段树维护每个点的操作. 离线处理完向上合并就好了 luogu倍增lca被卡了5分.....于是用rmq维护.... 常 ...

  4. P4556 [Vani有约会]雨天的尾巴 (线段树合并)

    P4556 [Vani有约会]雨天的尾巴 题意: 首先村落里的一共有n座房屋,并形成一个树状结构.然后救济粮分m次发放,每次选择两个房屋(x,y),然后对于x到y的路径上(含x和y)每座房子里发放一袋 ...

  5. 「Luogu4556」Vani有约会-雨天的尾巴

    「Luogu4556」Vani有约会-雨天的尾巴 传送门 很显然可以考虑树上差分+桶,每次更新一条链就是把这条链上的点在桶对应位置打上 \(1\) 的标记, 最后对每个点取桶中非零值的位置作为答案即可 ...

  6. [题解] P4556 [Vani有约会]雨天的尾巴

    [题解] P4556 [Vani有约会]雨天的尾巴 ·题目大意 给定一棵树,有m次修改操作,每次修改 \(( x\) \(y\) \(z )\) 表示 \((x,y)\) 之间的路径上数值 \(z\) ...

  7. 洛谷P4556 [Vani有约会]雨天的尾巴(线段树合并)

    题目背景 深绘里一直很讨厌雨天. 灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切. 虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连根拔起,以及田地 ...

  8. [Vani有约会]雨天的尾巴

    嘟嘟嘟 看到链上操作,自然想到树剖. 先考虑序列上的问题:那么区间修改可以用差分.所以我们把操作拆成\(L\)和\(R + 1\)两个点,然后离线.排序后扫一遍,用线段树维护数量最多的颜色是哪一个. ...

  9. [Vani有约会]雨天的尾巴(树上差分+线段树合并)

    首先村落里的一共有n座房屋,并形成一个树状结构.然后救济粮分m次发放,每次选择两个房屋(x,y),然后对于x到y的路径上(含x和y)每座房子里发放一袋z类型的救济粮. 然后深绘里想知道,当所有的救济粮 ...

随机推荐

  1. Quartz.NET 作业调度使用

    Quartz.NET的使用方法有很多,今天使用Quartz.NET3.0.6的时候发现和2.0版本的语法不太一样,百度上找了一圈也没有找到解决办法 后来在GitHub上下载源代码解决了 实现每隔10s ...

  2. Git学习手记

    直接使用github的客户端即可 1.简介 集中化的版本控制系统( Centralized Version Control Systems,简称 CVCS )应运而生.这类系统,诸如 CVS,Subv ...

  3. (转)Linux网络状态工具ss命令使用详解

    Linux网络状态工具ss命令使用详解 原文:http://www.landui.com/help/show-5991.html ss 是 socket statistics 的缩写.顾名思义,ss ...

  4. Oracle 基础系列之1.2 oracle的基本使用

    在1.1的安装过程完成之后,进入第二部分,oracle的基本使用,在使用前,首先测试下,oracle是否安装成功,以及检查oracle的关键服务时候开启 1.首先测试下,oracle是否安装成功 打开 ...

  5. jQuery遍历祖先元素:parentsUntil

    Description: Get the ancestors of each element in the current set of matched elements, up to but not ...

  6. JVM 类加载全过程

    类加载机制 - JVM把class文件加载到内存中 并对数据进行 校验,解析,初始化,最终形成JVM可以直接使用的java类型的过程 详细过程  加载→ 验证→ 准备→ 解析 → 初始化→ 使用 → ...

  7. SqlServer系统表认识和操作

    地址:http://technet.microsoft.com/zh-cn/library/ms178551.aspx 一.sys.sysindexkeys 包含有关数据库的索引中的键或列的信息.(表 ...

  8. (0!=0)==true? 记一个匪夷所思的问题

    最近换了份工作,公司的开发框架是基于SSH自己搭建的.这个问题是我在解决一个需求的时候遇到的,其实解决这个疑惑的过程也就是读框架源码的过程,特此记录一下. 问题:ba.getState()!=CbBa ...

  9. redis的有序集合(Sorted Sets)数据类型

    和Sets相比,Sorted Sets增加了一个权重参数score,使得集合中的元素能够按score进行有序排列,比如一个存储全班同学成绩的Sorted Sets,其集合value可以是同学的学号,而 ...

  10. Java transient和volatile关键字

    关键字Volatile Volatile修饰的成员变量在每次被线程访问时,都强迫从主内存中重读该成员变量的值.而且,当成员变量发生变化时,强迫线程将变化值回写到主内存.这样在任何时刻,两个不同的线程总 ...