[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. wepy的第一个demo

    一.在node中安装相应的模块文件,查看文档 二.案例 json部分 { "usingComponents": { "van-button": ".. ...

  2. Python之路-变量和基本数据类型详解(变量、数据类型、)

    一.注释 注释的作用: 增加程序的可读性 作为调试用 提高团队的合作效率 注释的分类 1.单行注释 以井号(#)开头,右边的所有内容当做说明 2.多行注释 以三对单引号(’’’注释内容’’’)将注释包 ...

  3. Azkaban 2.5.0的详细安装过程

    准备下载Azkaban2.5.0:https://azkaban.github.io/downloads.htm 准备插件: 一.MySQL安装与配置 启动数据库并查看状态:sudo service ...

  4. 结合pychrom与selenium实现页面自动登录

    缘起 一直在浏览器里用Katalon插件录制一些常用的流程,以减少重复操作,也就自然而然想自己搞搞自动化测试,但无奈登录一关跨不过去,就无法串起来.(不想让开发添加万能验证码的功能)首先想到的是识别验 ...

  5. python os 常用命令

    转载:http://www.cnblogs.com/kaituorensheng/archive/2013/03/18/2965766.html python编程时,经常和文件.目录打交道,这是就离不 ...

  6. dirname 显示文件或目录路径

    1. 命令功能 dirname 去除文件名中非目录部分,仅显示与目录有关部分.dirname读取指定路径名保留最后一个/及其后面部分的字符,删除其他部分,并把结果到标准输出.如果最后一个/后无字符,d ...

  7. Jquery异步上传文件

    我想通过jQuery异步上传文件,这是我的HTML: 1 2 3 <span>File</span> <input type="file" id=&q ...

  8. dict常用操作

    dict是python中的常用数据结构,应该尽量掌握其使用方法 """ 初始化一个dict的四种方式: 1. dict() -> 创建一个空的dict 2. dic ...

  9. python学习笔记(六)函数

    1.函数是什么? 定义:函数是指一组语句的集合通过一个名字(函数名)封装起来,只需调用函数名即可. 2.函数的好处: 简化代码 提高代码的复用性 代码可扩展 3.python中函数的定义: 定义函数使 ...

  10. c#Main()方法,java 是小写main

    main 方法,staitc 静态关键首字母大写,区分大小写,java 是main小写,返回值 ,vodi,int参数:可选static void Main(string[] args){ }