题目大意:

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. is assembler instruction and machine instuction atomic

    1 assembler instruction depends,有的汇编指令会被assemble成多条机器指令. 2 机器指令 depends,有的机器指令也不是atomic的. 所以,不要希望在单条 ...

  2. S-形函数广泛应用于ANN 的激活函数

    Logistic function hyperbolic tangent   arctangent function   Gudermannian function   Error function ...

  3. Linux根目录下重要文件夹

    bin存放系统命令的目录,普通用户和超级用户都可以执行,不过放在这里的命令在单用户模式下也可以执行 sbin保存和系统环境设置相关的命令,只有超级用户可以使用这些命令进行系统环境的设置,但有些命令可以 ...

  4. Symfony 使用KnpTimeBundle

    使用time_diff时出现:diff.ago.hour; 解决:1:引入"knplabs/knp-time-bundle": "^1.7",https://g ...

  5. 4G U盘版64位bitcoin专用挖矿操作系统

    这个操作系统是基于linux的操作系统,采用的ubuntu平台打造,所有的软件都已经安装齐备,是一个bitcoin专用挖矿操作系统,是64位的,对于显卡数量基本上没有限制,前提是你的主板支持足够多的显 ...

  6. HTML 5 本地存储

    <!DOCTYPE HTML> <html lang="en-US"> <head> <meta charset="UTF-8& ...

  7. python3 内置常用函数系列一

    python3 内置了一系列的常用函数, python英文官方文档详细说明:点击查看, 为了方便查看,将内置常用的函数的记录一下来. Python3版本所有的内置函数: 1.abs() print(a ...

  8. 全屏滚动插件之 fullpage.js

    前言:做移动端网页下滑/点击切换到下一页的效果,采用了fullpage,js最新的版本 https://cdnjs.cloudflare.com/ajax/libs/fullPage.js/3.0.4 ...

  9. 【并查集】关押罪犯(BSOJ2809)

    Description S城现有两座监狱,一共关押着N名罪犯,编号分别为1~N.他们之间的关系自然也极不和谐.很多罪犯之间甚至积怨已久,如果客观条件具备则随时可能爆发冲突.我们用“怨 气值”(一个正整 ...

  10. 20145229吴姗珊 《Java程序设计》小总结

    20145229吴姗珊 <Java程序设计>小总结 教材学习内容总结 由于今天考试考到了操作题,根本无从下手,然后才意识到原来之前的学习都是蜻蜓点水,一味的把学习建立在给老师学,为家长学的 ...