[BZOJ3307] 雨天的尾巴(树上差分+线段树合并)

题面

给出一棵N个点的树,M次操作在链上加上某一种类别的物品,完成所有操作后,要求询问每个点上最多物品的类型。

N, M≤100000

分析

考虑树上差分。对于每条链(x,y),我们在x,y打一个+标记,lca(x,y)和lca(x,y)的父亲打一个-标记。然后在每个节点建立一棵权值线段树,下标v维护物品v的个数。如果有物品v,就把下标为v的位置+1,如果有-标记,就-1.线段树push_up的时候可以计算出最多物品的类型

然后从下往上线段树合并,合并到某个节点的时候就更新该节点的答案。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 100000
#define maxlogn 60
using namespace std;
int n,m;
struct edge{
int from;
int to;
int next;
}E[maxn*2+5];
int sz=1;
int head[maxn+5];
void add_edge(int u,int v){
sz++;
E[sz].from=u;
E[sz].to=v;
E[sz].next=head[u];
head[u]=sz;
}
int deep[maxn+5];
int anc[maxn+5][maxlogn+5];
void dfs1(int x,int fa){
deep[x]=deep[fa]+1;
anc[x][0]=fa;
for(int i=1;i<=maxlogn;i++) anc[x][i]=anc[anc[x][i-1]][i-1];
for(int i=head[x];i;i=E[i].next){
int y=E[i].to;
if(y!=fa){
dfs1(y,x);
}
}
}
int lca(int x,int y){
if(deep[x]<deep[y]) swap(x,y);
for(int i=maxlogn;i>=0;i--){
if(deep[anc[x][i]]>=deep[y]){
x=anc[x][i];
}
}
if(x==y) return x;
for(int i=maxlogn;i>=0;i--){
if(anc[x][i]!=anc[y][i]){
x=anc[x][i];
y=anc[y][i];
}
}
return anc[x][0];
} struct segment_tree{
#define lson(x) (tree[x].ls)
#define rson(x) (tree[x].rs)
struct node{
int ls;
int rs;
int cnt;
int id;
}tree[maxn*maxlogn+5];
int ptr;
void push_up(int x){
if(tree[lson(x)].cnt>tree[rson(x)].cnt){
tree[x].cnt=tree[lson(x)].cnt;
tree[x].id=tree[lson(x)].id;
}else if(tree[lson(x)].cnt==tree[rson(x)].cnt){
tree[x].cnt=tree[lson(x)].cnt;
tree[x].id=min(tree[lson(x)].id,tree[rson(x)].id);
}else{
tree[x].cnt=tree[rson(x)].cnt;
tree[x].id=tree[rson(x)].id;
}
}
void update(int &x,int upos,int uval,int l,int r){
if(!x) x=++ptr;
if(l==r){
tree[x].cnt+=uval;
tree[x].id=l;
return;
}
int mid=(l+r)>>1;
if(upos<=mid) update(tree[x].ls,upos,uval,l,mid);
else update(tree[x].rs,upos,uval,mid+1,r);
push_up(x);
}
int merge(int x,int y,int l,int r){
if(!x||!y) return x+y;
if(l==r){
tree[x].cnt+=tree[y].cnt;
tree[x].id=l;
return x;
}
int mid=(l+r)>>1;
tree[x].ls=merge(tree[x].ls,tree[y].ls,l,mid);
tree[x].rs=merge(tree[x].rs,tree[y].rs,mid+1,r);
push_up(x);
return x;
}
}T;
int root[maxn+5];
int ans[maxn+5]; int maxz;
struct query{
int x;
int y;
int z;
}q[maxn+5];
void dfs2(int x,int fa){
for(int i=head[x];i;i=E[i].next){
int y=E[i].to;
if(y!=fa){
dfs2(y,x);
root[x]=T.merge(root[x],root[y],1,maxz);
}
}
if(T.tree[root[x]].cnt) ans[x]=T.tree[root[x]].id;
else ans[x]=0;
}
int main(){
int u,v;
scanf("%d %d",&n,&m);
for(int i=1;i<n;i++){
scanf("%d %d",&u,&v);
add_edge(u,v);
add_edge(v,u);
}
dfs1(1,0);
maxz=0;
for(int i=1;i<=m;i++){
scanf("%d %d %d",&q[i].x,&q[i].y,&q[i].z);
maxz=max(q[i].z,maxz);
}
for(int i=1;i<=m;i++){
int x=q[i].x,y=q[i].y,z=q[i].z,lc=lca(x,y);
T.update(root[x],z,1,1,maxz);
T.update(root[y],z,1,1,maxz);
T.update(root[lc],z,-1,1,maxz);
T.update(root[anc[lc][0]],z,-1,1,maxz);
}
dfs2(1,0);
for(int i=1;i<=n;i++){
printf("%d\n",ans[i]);
}
}

[BZOJ3307] 雨天的尾巴(树上差分+线段树合并)的更多相关文章

  1. BZOJ 3307 雨天的尾巴 (树上差分+线段树合并)

    题目大意:给你一棵树,树上一共n个节点,共m次操作,每次操作给一条链上的所有节点分配一个权值,求所有节点被分配到所有的权值里,出现次数最多的权值是多少,如果出现次数相同就输出最小的. (我辣鸡bzoj ...

  2. bzoj3307 雨天的尾巴 题解(线段树合并+树上差分)

    Description N个点,形成一个树状结构.有M次发放,每次选择两个点x,y 对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.完成 所有发放后,每个点存放最多的是哪种物品. Input ...

  3. [Luogu5327][ZJOI2019]语言(树上差分+线段树合并)

    首先可以想到对每个点统计出所有经过它的链的并所包含的点数,然后可以直接得到答案.根据实现不同有下面几种方法.三个log:假如对每个点都存下经过它的链并S[x],那么每新加一条路径进来的时候,相当于在路 ...

  4. 2018.08.28 洛谷P4556 [Vani有约会]雨天的尾巴(树上差分+线段树合并)

    传送门 要求维护每个点上出现次数最多的颜色. 对于每次修改,我们用树上差分的思想,然后线段树合并统计答案就行了. 注意颜色很大需要离散化. 代码: #include<bits/stdc++.h& ...

  5. bzoj 3307: 雨天的尾巴【树剖lca+树上差分+线段树合并】

    这居然是我第一次写线段树合并--所以我居然在合并的时候加点结果WAWAWAMLEMLEMLE--!ro的时候居然直接指到la就行-- 树上差分,每个点建一棵动态开点线段树,然后统计答案的时候合并即可 ...

  6. P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并 (树上差分+线段树合并)

    显然的树上差分问题,最后要我们求每个点数量最多的物品,考虑对每个点建议线段树,查询子树时将线段树合并可以得到答案. 用动态开点的方式建立线段树,注意离散化. 1 #include<bits/st ...

  7. 【bzoj3307】雨天的尾巴 权值线段树合并

    题目描述 N个点,形成一个树状结构.有M次发放,每次选择两个点x,y,对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.完成所有发放后,每个点存放最多的是哪种物品. 输入 第一行数字N,M接下来 ...

  8. Luogu5327 ZJOI2019语言(树上差分+线段树合并)

    暴力树剖做法显然,即使做到两个log也不那么优美. 考虑避免树剖做到一个log.那么容易想到树上差分,也即要对每个点统计所有经过他的路径产生的总贡献(显然就是所有这些路径端点所构成的斯坦纳树大小),并 ...

  9. [NOIP2016]天天爱跑步(树上差分+线段树合并)

    将每个人跑步的路径拆分成x->lca,lca->y两条路径分别考虑: 对于在点i的观察点,这个人(s->t)能被观察到的充要条件为: 1.直向上的路径:w[i]=dep[s]-dep ...

随机推荐

  1. javascript Math取整&获取随机数

    1.方法介绍 Math.ceil(n) 上取整,大于等于n返回与它最接近的整数 Math.floor(n) 下取整,小于等于n返回与它最接近的整数 Math.round(n) 四舍五入取整 Math. ...

  2. vue中对于图片是否正常加载的思考

    问题:由于业务需要,我们需要判断图片能否正常的加载,如果未正常加载的话,需要显示一张默认图片: 方案:1,由于后台返回的是一个图片id数组,例如 imgList=['343313131','21333 ...

  3. HBase构架原理

    HBase的概念: HBase在生态圈位置 HBase与HDFS对比 HBase与关系型数据库的比较 HBase表的特点: 4)任意模式:每一行都有一个可排序的主键和任意多的列,列可以根据自己的需要动 ...

  4. Tcp客户端构建流程

    tcp客户端构建流程 tcp的客户端要比服务器端简单很多,如果说服务器端是需要自己买手机.查手机卡.设置铃声.等待别人打电话流程的话,那么客户端就只需要找一个电话亭,拿起电话拨打即可,流程要少很多 示 ...

  5. 一、ffmpeg理论

    一.ffmpeg三件套 ffprobe看文件属性 ffmpeg 编转码 ffplay 播放器 C:\Users\Bruce\Desktop\Temp\ffmpeg\ffmpeg\ToolPack\ff ...

  6. Shell脚本的fork炸弹

    #!bin/bash#功能:快速消耗计算机资源,致使计算机死机#作者:liusingbon#定义函数名为.(点), 函数中递归调用自己并放入后台执行function . { .|.& };.

  7. CentOS 7系统安装nginx+php

    安装介绍1.系统环境CentOS7 2.nginx版本1.12 3.PHP版本7.2 下载地址 4.MySQL版本5.7 安装nginx添加centos7的 nginx yum源 然后执行安装 sud ...

  8. cat 合并文件或查看文件内容

    1.命令功能 cat 合并文件或者查看文件内容. 2.语法格式 cat   option    file 参数说明 参数 参数说明 -n 打印文本,并显示每行行号并且空白行也同样包括 -b 与-n用法 ...

  9. CSS3 结构性伪类选择器(1)

    1.CSS3 结构性伪类选择器—root :root选择器就是匹配元素E所在文档的根元素.在HTML文档中,根元素始终是<html>. “:root”选择器等同于<html>元 ...

  10. bzoj5518 & loj3046 「ZJOI2019」语言 线段树合并+树链的并

    题目传送门 https://loj.ac/problem/3046 题解 首先问题就是问有多少条路径是给定的几条路径中的一条的一个子段. 先考虑链的做法. 枚举右端点 \(i\),那么求出 \(j\) ...