bzoj 3307: 雨天的尾巴 线段树合并
题目大意:
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: 雨天的尾巴 线段树合并的更多相关文章
- bzoj 3307: 雨天的尾巴【树剖lca+树上差分+线段树合并】
这居然是我第一次写线段树合并--所以我居然在合并的时候加点结果WAWAWAMLEMLEMLE--!ro的时候居然直接指到la就行-- 树上差分,每个点建一棵动态开点线段树,然后统计答案的时候合并即可 ...
- [Vani有约会]雨天的尾巴 线段树合并
[Vani有约会]雨天的尾巴 LG传送门 线段树合并入门好题. 先别急着上线段树合并,考虑一下这题的暴力.一看就是树上差分,对于每一个节点统计每种救济粮的数量,再一遍dfs把差分的结果统计成答案.如果 ...
- 【BZOJ3307】雨天的尾巴 线段树合并
[BZOJ3307]雨天的尾巴 Description N个点,形成一个树状结构.有M次发放,每次选择两个点x,y对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.完成所有发放后,每个点存放最多 ...
- BZOJ3307雨天的尾巴——线段树合并
题目描述 N个点,形成一个树状结构.有M次发放,每次选择两个点x,y对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.完成所有发放后,每个点存放最多的是哪种物品. 输入 第一行数字N,M接下来N ...
- P4556 雨天的尾巴 线段树合并
使用线段树合并,每个节点维护一棵权值线段树,下标为救济粮种类,区间维护数量最多的救济粮编号(下标).所以每个节点答案即为\(tre[rot[x]]\). 然后运用树上点的差分思想,对于分发路径\(u, ...
- 洛谷P4556 [Vani有约会]雨天的尾巴(线段树合并)
题目背景 深绘里一直很讨厌雨天. 灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切. 虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连根拔起,以及田地 ...
- [BZOJ 1483] [HNOI2009] 梦幻布丁 (线段树合并)
[BZOJ 1483] [HNOI2009] 梦幻布丁 (线段树合并) 题面 N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色.例如颜色分别为1 ...
- 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). 复杂度看起来不高, ...
- Bzoj 3307 雨天的尾巴(线段树合并+树上差分)
C. 雨天的尾巴 题目描述 N个点,形成一个树状结构.有M次发放,每次选择两个点x,y对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.完成所有发放后,每个点存放最多的是哪种物品. 输入格式 第 ...
随机推荐
- linux 下配置jdk
去java官方地址下载相应的源码包我下载的是1.8.0放在usr/local目录下 export JAVA_HOME=/usr/local/jdk1.8.0export PATH=$JAVA_HOME ...
- 与webView进行交互,webView小记
本文转载至 http://www.verydemo.com/demo_c101_i46895.html 一.与webView进行交互,调用web页面中的需要传参的函数时,参数需要带单引号,或者双引号( ...
- One Person Game(概率+数学)
There is a very simple and interesting one-person game. You have 3 dice, namelyDie1, Die2 and Die3. ...
- J - 组合
J - 组合 Time Limit:1000MS Memory Limit:10000KB 64bit IO Format:%lld & %llu Description 有两 ...
- 九度OJ 1180:对称矩阵 (矩阵计算)
时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:2637 解决:1354 题目描述: 输入一个N维矩阵,判断是否对称. 输入: 输入第一行包括一个数:N(1<=N<=100),表 ...
- iOS源代码管理svn
01. SVN介绍 SVN 是集中式源代码管理工具 概念: 1> Repository 代码仓库,保存代码的仓库 2> Server 服务器,保存所有版本的代码仓库 3&g ...
- php在web端播放amr语音(如微信语音)
在使用微信JSSDK的上传下载语音接口时,发现一个问题: 下载的语音在iPhone上不能播放,测试了之后原因竟然是: 微信接口返回的音频内容是amr格式的,但iPhone不支持播放此类型格式. 那么转 ...
- ThoughtWorks(中国) 程序员读书雷达
ThoughtWorks(中国)程序员读书雷达 软件业的特点是变化.若要提高软件开发的技能,就必须跟上技术发展的步伐.埋首醉心于项目开发与实战,固然能够锤炼自己的开发技巧,却难免受限于经验与学识.世界 ...
- linux shell 字符串操作(长度,查找,替换)
感谢原创,文章很有帮助. 转自:http://www.cnblogs.com/chengmo/archive/2010/10/02/1841355.html 一.判断读取字符串值 表达式 含义 ${v ...
- LeetCode:三个数的最大乘积【628】
LeetCode:三个数的最大乘积[628] 题目描述 给定一个整型数组,在数组中找出由三个数组成的最大乘积,并输出这个乘积. 示例 1: 输入: [1,2,3] 输出: 6 示例 2: 输入: [1 ...