题目大意:

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. Spring 和 filter

    标题是 spring和filter,但是这里却是说的spring MVC 项目中需要用到filter,filter中需要用到spring实例化的bean,于是为了简化就形成spring和filter了 ...

  2. 【BZOJ3743】[Coci2015]Kamp 树形DP

    [BZOJ3743][Coci2015]Kamp Description 一颗树n个点,n-1条边,经过每条边都要花费一定的时间,任意两个点都是联通的. 有K个人(分布在K个不同的点)要集中到一个点举 ...

  3. java servlet中上传文件的简单实现(基于第三方jar)

    这里的案例使用了两种文件上传的组件.分别介绍 1.使用JSPSmartUpload完成上传 package test_servlet_package; import java.io.File; imp ...

  4. HTML元素嵌套关系

  5. Android笔记之AsyncTask

    AsyncTask中的4个回调 onPreExecute(),在doInBackground(Params...)之前运行在UI线程中 onPostExecute(Result),在doInBackg ...

  6. [Tjoi2018]数学计算

    [Tjoi2018]数学计算 BZOJ luogu 线段树分治 是不是想问为什么不暴力做? 模数没说是质数,所以不一定有逆元. 然后就是要每次build一下把线段树权值init成1, 博猪不知道为什么 ...

  7. 7 Javascript:表单与验证-非空验证

    表单提交前要检查数据的合法性 在要对表单里的数据进行验证的时候,能够利用getElementById()来訪问网页上不论什么一个元素 每一个表单域都有一个form对象,可被传给不论什么验证表单数据的函 ...

  8. SpringMVC请求流程

    Spring结构图 SpringMVC请求流程图 SpringMVC请求流程图语述: request--->DispatcherServler(中央调度器/前端控制器)---> Handl ...

  9. CSS选择器(一)

    一.CSS 元素选择器 最常见的 CSS 选择器是元素选择器.换句话说,文档的元素就是最基本的选择器. 如果设置 HTML 的样式,选择器通常将是某个 HTML 元素,比如 p.h1.em.a,甚至可 ...

  10. UVALive - 7427 the math 【二分匹配】

    题目链接 https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_ ...