题目大意:

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

题解:

首先我们为每一个节点都开一个线段树

然后我们进行树上路径差分:

对于一个从u->v的路径,我们将其分解为:

u->1 , v->1,fa[lca(u,v)] -> 1

lca表示最近公共祖先,fa[x]为x的父亲节点

所以我们需要附加权值

我们可以在u的线段树上加上1

在v的线段树上加上1

在lca(u,v)的线段树上加上-1

在fa[lca(u,v)]的线段树上加上-1

然后我们自叶子向根依次进行线段树的合并即可

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
x=0;char ch;bool flag = false;
while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
inline int cat_max(const int &a,const int &b){return a>b ? a:b;}
inline int cat_min(const int &a,const int &b){return a<b ? a:b;}
const int maxn = 110010;
const int inf = 0x3f3f3f3f;
struct Edge{
int to,next;
}G[maxn<<1];
int head[maxn],cnt;
void add(int u,int v){
G[++cnt].to = v;
G[cnt].next = head[u];
head[u] = cnt;
}
#define v G[i].to
int siz[maxn],son[maxn],fa[maxn],dep[maxn];
int top[maxn];
void dfs(int u){
siz[u] = 1;
for(int i = head[u];i;i=G[i].next){
if(v == fa[u]) continue;
dep[v] = dep[u] + 1;
fa[v] = u;
dfs(v);
siz[u] += siz[v];
if(siz[son[u]] < siz[v]) son[u] = v;
}
}
void dfs(int u,int tp){
top[u] = tp;
if(son[u]) dfs(son[u],tp);
for(int i = head[u];i;i=G[i].next){
if(v == fa[u] || v == son[u]) continue;
dfs(v,v);
}
}
#undef v
inline int lca(int u,int v){
while(top[u] != top[v]){
if(dep[top[u]] < dep[top[v]]) swap(u,v);
u = fa[top[u]];
}return dep[u] < dep[v] ? u : v;
}
struct Node{
Node *ch[2];
int mx,id;
void update();
}*null;
void Node::update(){
mx = max(ch[0]->mx,ch[1]->mx);
if(mx == ch[0]->mx) id = ch[0]->id;
else if(mx == ch[1]->mx) id = ch[1]->id;
}
Node mem[maxn<<6],*it;
inline void init(){
it = mem;null = it++;
null->ch[0] = null->ch[1] = null;
null->mx = -inf;null->id = -1;
}
inline Node* newNode(){
Node *p = it++;p->ch[0] = p->ch[1] = null;
p->mx = -inf;p->id = 0;return p;
}
void merge(Node* &x,Node *y){
if(x == null){x = y;return;}
if(x->ch[0] == null || y->ch[0] == null){
x->ch[0] = y->ch[0] == null ? x->ch[0] : y->ch[0];
}else merge(x->ch[0],y->ch[0]);
if(x->ch[1] == null || y->ch[1] == null){
x->ch[1] = y->ch[1] == null ? x->ch[1] : y->ch[1];
}else merge(x->ch[1],y->ch[1]);
if(x->ch[0] == x->ch[1] && x->ch[0] == null){
x->mx += y->mx;
}else x->update();
return;
}
void insert(Node* &p,int l,int r,int pos,int d){
if(p == null) p = newNode();
if(l == r){
p->id = pos;
if(p->mx == -inf) p->mx = d;
else p->mx += d;
return;
}
int mid = (l+r) >> 1;
if(pos <= mid) insert(p->ch[0],l,mid,pos,d);
else insert(p->ch[1],mid+1,r,pos,d);
p->update();return;
}
Node *root[maxn];
int anss[maxn];
#define v G[i].to
void dfss(int u,int fa){
for(int i = head[u];i;i=G[i].next){
if(v == fa) continue;
dfss(v,u);
merge(root[u],root[v]);
}
if(root[u]->id != -1) anss[u] = root[u]->id;
}
#undef v
int main(){
init();
int n,m;read(n);read(m);
for(int i=0;i<=n;++i) root[i] = null;
for(int i=1,u,v;i<n;++i){
read(u);read(v);
add(u,v);add(v,u);
}dfs(1);dfs(1,1);
for(int i=1,u,v,d;i<=m;++i){
read(u);read(v);read(d);
insert(root[u],1,1e9,d,1);
insert(root[v],1,1e9,d,1);
int lc = lca(u,v);
insert(root[lc],1,1e9,d,-1);
if(lc != 1) insert(root[fa[lc]],1,1e9,d,-1);
}dfss(1,0);
for(int i=1;i<=n;++i){
printf("%d\n",anss[i]);
}
getchar();getchar();
return 0;
}

bzoj 3307: 雨天的尾巴 线段树合并的更多相关文章

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

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

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

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

  3. 【BZOJ3307】雨天的尾巴 线段树合并

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

  4. BZOJ3307雨天的尾巴——线段树合并

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

  5. P4556 雨天的尾巴 线段树合并

    使用线段树合并,每个节点维护一棵权值线段树,下标为救济粮种类,区间维护数量最多的救济粮编号(下标).所以每个节点答案即为\(tre[rot[x]]\). 然后运用树上点的差分思想,对于分发路径\(u, ...

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

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

  7. [BZOJ 1483] [HNOI2009] 梦幻布丁 (线段树合并)

    [BZOJ 1483] [HNOI2009] 梦幻布丁 (线段树合并) 题面 N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色.例如颜色分别为1 ...

  8. BZOJ 3307: 雨天的尾巴( LCA + 线段树合并 )

    路径(x, y) +z : u处+z, v处+z, lca(u,v)处-z, fa(lca)处-z, 然后dfs一遍, 用线段树合并. O(M log M + M log N). 复杂度看起来不高, ...

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

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

随机推荐

  1. 【BZOJ2119】股市的预测 后缀数组+分块

    [BZOJ2119]股市的预测 Description 墨墨的妈妈热爱炒股,她要求墨墨为她编写一个软件,预测某只股票未来的走势.股票折线图是研究股票的必备工具,它通过一张时间与股票的价位的函数图像清晰 ...

  2. Linux学习路线指南

    转载的,感觉写的挺好的,我自己知识复制了下,忘记了转载地址,抱歉! Linux学习路线指南 很多同学接触Linux不多,对Linux平台的开发更是一无所知.而现在的趋势越来越表明,作为一个优秀的软件开 ...

  3. Unicode Category

    Lu(letter,uppercase):大写字母. Ll(letter.lowercase):小写字母. Lt(letter,titlecase):词首字母大写的字母. Lm(letter,modi ...

  4. Python读属性文件

    # coding:utf-8 class Properties: def __init__(self, file_name): self.file_name = file_name self.prop ...

  5. android禁止横屏

    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

  6. window 上创建 .gitignore文件

    由于 git默认不上传空文件夹,如果需要上传空文件夹,那么需要这样上传空文件,官方给出这样的做法~~ (需要创建.gitignore文件) 在linux 上比较好操作了,这里说下在window 上 创 ...

  7. java使用poi读写Excel

    package com.demo.excel; import com.demo.pojo.Student; import org.apache.poi.hssf.usermodel.HSSFCell; ...

  8. spark 在yarn执行job时一直抱0.0.0.0:8030错误

    近日新写完的spark任务放到yarn上面执行时,在yarn的slave节点中一直看到报错日志:连接不到0.0.0.0:8030 . The logs are as below: 2014-08-11 ...

  9. ABAP下载服务器文件到本机

    转自http://blog.sina.com.cn/s/blog_701594f40100l8ml.html ABAP:下载服务器文件到本机 对服务器的文件进行读写操作,SAP提供了OPEN DATA ...

  10. php验证复选框的小例子

    发布:thatboy   来源:Net     [大 中 小] 本文介绍一个简单的php实例,通过代码验证复选框值的有效性,有需要的朋友,可以参考下. 验证复选框的php代码,如下: <?php ...