[BZOJ3307]:雨天的尾巴(LCA+树上差分+权值线段树)
题目传送门
题目描述
N个点,形成一个树状结构。有M次发放,每次选择两个点x,y对于x到y的路径上(含x,y)每个点发一袋Z类型的物品。完成所有发放后,每个点存放最多的是哪种物品。
输入格式
第一行数字N,M
接下来N-1行,每行两个数字a,b,表示a与b间有一条边
再接下来M行,每行三个数字x,y,z如题
输出格式
输出有N行
每i行的数字表示第i个点存放最多的物品是哪一种,如果有
多种物品的数量一样,输出编号最小的。如果某个点没有物品则输出0。
样例
样例输入:
20 50
8 6
10 6
18 6
20 10
7 20
2 18
19 8
1 6
14 20
16 10
13 19
3 14
17 18
11 19
4 11
15 14
5 18
9 10
12 15
11 14 87
12 1 87
14 3 84
17 2 36
6 5 93
17 6 87
10 14 93
5 16 78
6 15 93
15 5 16
11 8 50
17 19 50
5 4 87
15 20 78
1 17 50
20 13 87
7 15 22
16 11 94
19 8 87
18 3 93
13 13 87
2 1 87
2 6 22
5 20 84
10 12 93
18 12 87
16 10 93
8 17 93
14 7 36
7 4 22
5 9 87
13 10 16
20 11 50
9 16 84
10 17 16
19 6 87
12 2 36
20 9 94
9 2 84
14 1 94
5 5 94
8 17 16
12 8 36
20 17 78
12 18 50
16 8 94
2 19 36
10 18 36
14 19 50
4 12 50
(有点长……)
样例输出:
87
36
84
22
87
87
22
50
84
87
50
36
87
93
36
94
16
87
50
50
数据范围与提示
1≤N,M≤100000
1≤a,b,x,y≤N
1≤z≤109
题解
看到这道题我首先想到了这道题:BZOJ3631。
对于上面这道题,如果你还没有思路的话可以看一下这篇博客:[BZOJ3631]:[JLOI2014]松鼠的新家(LCA+树上差分)。
然后,我就立马想到了先求出LCA然后进行树上差分,但是这两道题的区别在于,这道题的物品种类不止一个,而且数还不小,显然不能单纯的用树上差分来解决。
于是考虑用权值线段树维护,先将物品种类排序,然后看利用树上差分更新权值线段树,之后递归合并统计答案即可。
代码时刻
#include<bits/stdc++.h>
using namespace std;
struct rec//存边
{
int nxt;
int to;
}e[200000];
struct qwq//存操作
{
int x;
int y;
int z;
}question[100001];
int head[100001],cnt;
int fa[100001][20];
int root[100001],trfal[10000001],trsum[10000001],ls[10000001],rs[10000001],sum,tot;
int dfsv[100001];
int depth[100001];
int que[100001];
bool cmp(qwq a,qwq b){return a.z<b.z;}//按物品排序问题
void add(int x,int y)//建边
{
e[++cnt].nxt=head[x];
e[cnt].to=y;
head[x]=cnt;
}
void dfs(int x)//dfs预处理父亲和深度
{
dfsv[++dfsv[0]]=x;
for(int i=head[x];i;i=e[i].nxt)
if(e[i].to!=fa[x][0])
{
fa[e[i].to][0]=x;
depth[e[i].to]=depth[x]+1;
for(int j=1;j<=18;j++)
fa[e[i].to][j]=fa[fa[e[i].to][j-1]][j-1];
dfs(e[i].to);
}
}
int LCA(int x,int y)//倍增求LCA
{
if(depth[x]<depth[y])swap(x,y);
for(int i=18;i>=0;i--)
if(depth[fa[x][i]]>=depth[y])x=fa[x][i];
if(x==y)return x;
for(int i=18;i>=0;i--)
if(fa[x][i]!=fa[y][i])
{
x=fa[x][i];
y=fa[y][i];
}
return fa[x][0];
}
void pushup(int x)
{
trfal[x]=max(trfal[ls[x]],trfal[rs[x]]);//维护的权值
trsum[x]=(trfal[ls[x]]>=trfal[rs[x]])?trsum[ls[x]]:trsum[rs[x]];//哪种物品
}
void insert(int &x,int k,int fl,int l,int r)
{
if(!x)x=++tot;
if(l==r)
{
trfal[x]+=fl;
trsum[x]=que[l];
return;
}
int mid=(l+r)>>1;
if(k<=mid)insert(ls[x],k,fl,l,mid);
else insert(rs[x],k,fl,mid+1,r);
pushup(x);
}
void ask(int &x,int fl,int l,int r)
{
if(!fl)return;
if(!x)
{
x=fl;
return;
}
if(l==r)
{
trfal[x]+=trfal[fl];
return;
}
int mid=(l+r)>>1;
ask(ls[x],ls[fl],l,mid);
ask(rs[x],rs[fl],mid+1,r);
pushup(x);
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)root[i]=i;
tot=n;
for(int i=1;i<n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
add(a,b);
add(b,a);
}
depth[1]=1;
dfs(1);
for(int i=1;i<=m;i++)
scanf("%d%d%d",&question[i].x,&question[i].y,&question[i].z);
sort(question+1,question+m+1,cmp);
for(int i=1;i<=m;i++)
{
int lca=LCA(question[i].x,question[i].y);
if(question[i].z>question[i-1].z)que[++sum]=question[i].z;
insert(root[question[i].x],sum,1,1,m);//树上差分操作
insert(root[question[i].y],sum,1,1,m);
insert(root[lca],sum,-1,1,m);
if(lca!=1)insert(root[fa[lca][0]],sum,-1,1,m);
}
for(int i=n;i>1;i--)
ask(root[fa[dfsv[i]][0]],root[dfsv[i]],1,m);
for(int i=1;i<=n;i++)
printf("%d\n",trsum[root[i]]);
return 0;
}
rp++
[BZOJ3307]:雨天的尾巴(LCA+树上差分+权值线段树)的更多相关文章
- [BZOJ3307] 雨天的尾巴(树上差分+线段树合并)
[BZOJ3307] 雨天的尾巴(树上差分+线段树合并) 题面 给出一棵N个点的树,M次操作在链上加上某一种类别的物品,完成所有操作后,要求询问每个点上最多物品的类型. N, M≤100000 分析 ...
- 【bzoj3307】雨天的尾巴 权值线段树合并
题目描述 N个点,形成一个树状结构.有M次发放,每次选择两个点x,y,对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.完成所有发放后,每个点存放最多的是哪种物品. 输入 第一行数字N,M接下来 ...
- HDU-6704 K-th occurrence (后缀自动机father树上倍增建权值线段树合并)
layout: post title: HDU-6704 K-th occurrence (后缀自动机father树上倍增建权值线段树合并) author: "luowentaoaa&quo ...
- 【bzoj4719】[Noip2016]天天爱跑步 权值线段树合并
题目描述 给出一棵n个点的树,以及m次操作,每次操作从起点向终点以每秒一条边的速度移动(初始时刻为0),最后对于每个点询问有多少次操作在经过该点的时刻为某值. 输入 第一行有两个整数N和M .其中N代 ...
- BZOJ 3110 ZJOI 2013 K大数查询 树套树(权值线段树套区间线段树)
题目大意:有一些位置.这些位置上能够放若干个数字. 如今有两种操作. 1.在区间l到r上加入一个数字x 2.求出l到r上的第k大的数字是什么 思路:这样的题一看就是树套树,关键是怎么套,怎么写.(话说 ...
- 线段树(单标记+离散化+扫描线+双标记)+zkw线段树+权值线段树+主席树及一些例题
“队列进出图上的方向 线段树区间修改求出总量 可持久留下的迹象 我们 俯身欣赏” ----<膜你抄> 线段树很早就会写了,但一直没有总结,所以偶尔重写又会懵逼,所以还是要总结一下. ...
- BZOJ_2161_布娃娃_权值线段树
BZOJ_2161_布娃娃_权值线段树 Description 小时候的雨荨非常听话,是父母眼中的好孩子.在学校是老师的左右手,同学的好榜样.后来她成为艾利斯顿第二 代考神,这和小时候培养的良好素质是 ...
- cf1073G Yet Another LCP Problem (SA+权值线段树)
反正先求一遍sa 然后这个问题可以稍微转化一下 默认比较A.B数组中元素的大小都是比较它们rank的大小,毕竟两个位置的LCP就是它们rank的rmq 然后每次只要求B[j]>=A[i]的LCP ...
- BZOJ3110[Zjoi2013]K大数查询——权值线段树套线段树
题目描述 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是 ...
随机推荐
- 给定一个数字n,生成n对可能的小括号组合
示例: 输入:n为3 输出:[ "((()))", "(()())" "(())()", "()(())", " ...
- js微信摇一摇功能以及api
一.摇一摇功能 <!DOCTYPE html> <html lang="en"> <head> <meta charset="U ...
- qscoj53(图的m着色问题)
题目链接:http://qscoj.cn/contest/12/problem/53/ 题意:中文题诶- 思路:n个点, 那么最多用n种颜色,所以我们可以枚举颜色种类1~n,然后再判断用 i 种颜色可 ...
- SpringBoot 2.0 集成 JavaMail ,实现异步发送邮件
一.JavaMail的核心API 1.API功能图解 2.API说明 (1).Message 类: javax.mail.Message 类是创建和解析邮件的一个抽象类 子类javax.mail.in ...
- slice方法可以将“类似数组的对象”变成真正的数组 (遇到时候再研究一次)
典型的“类似数组的对象”是函数的arguments对象,以及大多数 DOM 元素集,还有字符串. // arguments对象 function args() { return arguments } ...
- css-原理详解
语法 CSS 规则由两个主要的部分构成:选择器,以及一条或多条声明: 选择器一般使用id或者class选择器,声明由{}包含,每条声明由一个属性和一个值组成. .city { float:left; ...
- 牛客练习赛42A(字符串)
传送门 结论是:一定是选取最长的那个AB连续子串. 把题面要求的a*b + a + b转化一下成(a + 1)*(b + 1) - 1,即可发现如果选取前缀后缀不连续的两段作为答案,则显然有更优解,即 ...
- Single-use Stones Codeforces - 965D
https://codeforces.com/contest/965/problem/D 太神仙了...比E难啊.. 首先呢,根据题意,可以很容易的建出一个最大流模型 就是每个位置建一条边,容量限制为 ...
- Lock简介
digest synchronized已经提供了锁的功能,而且还是Java的内置特性,那为什么还要出现lock呢? 用一句话来讲就是——synchronized可以实现同步,但太死板了它的同步机制:l ...
- Linux 环境下安装 Nginx+php+mysql 开发环境
一直以来都没有养成记录学习的好习惯,我想从这么一天开始,把自己学习和工作中的经验和坑都记录下来.等到以后老的时候还有可以回忆的地方. 最近在学习linux,虽然已经玩linux很久了,但是没有怎么用心 ...