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
----------------------------------van美分界线----------------------------------
先%一发pa大佬考试A掉这题
%%%pa
考试时刚看到这题时觉得和之前的考试题松鼠的新家(此坑未填)很像,因为都是对树上的一条链进行修改操作
所以很容易想到树上差分(其实树剖也可以但蒟蒻博主并不会),具体讲就是将链的两端加一,将lca和lca父亲节点减一。
然后我们可以看到他是询问数量所以可以想到在每一个节点建一棵权值线段树来维护信息。
又看到1e9的范围瞬间吓尿,跑去码T1,其实只要离散化一下就可以,因此我们不仅需要维护每一个节点的最大值,还要维护最大值出现的位置,这样比较方便输出答案,建立对应关系即可。
最后dfs统计答案即可,就是从叶节点往上不断merge。
最后要注意的一点就是和线段树有关的数组一定要开大一些,本人亲测要1e5×60,临接表数组开二倍(都这时候了我还犯这么低级错误,真沙雕)。
回想一下这题也没那么难,但我还是断断续续调了得有5.6节课,沙雕错误百出。具体沙雕错误代码里都有注释(大佬自动忽略即可,勿喷)。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
const int N=1e5+;
int n,m;int tot;int t;
int first[N],nex[N*],to[N*],cnt,d[N],root[N**],v[N],f[N][],sum[N**],posm[N**],ls[N**],rs[N**],ans[N],x[N],yy[N],zz[N],num[N];
void add(int a,int b){
to[++tot]=b;nex[tot]=first[a];first[a]=tot;
}
void bfs(int x){
queue<int> q;
q.push(x);d[x]=;
while(!q.empty()){
int x=q.front();q.pop();
for(int i=first[x];i;i=nex[i]){
int y=to[i];
if(d[y]) continue;
d[y]=d[x]+;
f[y][]=x;
for(int j=;j<=t;j++)
f[y][j]=f[f[y][j-]][j-];
q.push(y);
}
}
}
int Lca(int x,int y){
if(d[y]<d[x]) swap(x,y);
for(int i=t;i>=;i--){
if(d[f[y][i]]>=d[x]) y=f[y][i];
}
if(x==y) return x;
for(int i=t;i>=;i--){
if(f[y][i]!=f[x][i]) x=f[x][i],y=f[y][i];
}
return f[x][];
}
void pushup(int x){
if(sum[ls[x]]>=sum[rs[x]]) sum[x]=sum[ls[x]],posm[x]=posm[ls[x]];
else sum[x]=sum[rs[x]],posm[x]=posm[rs[x]];
}
void update(int &x,int z,int add,int l,int r){
if(!x){
x=++cnt;
}
if(l==r){
sum[x]+=add;posm[x]=z/*z !l*/;
return;//void return sbsbsbsb
}
int mid=(l+r)>>;
if(z<=mid){
update(ls[x],z,add,l,mid);//
}
else update(rs[x],z,add,mid+,r);//递归儿子啊喂
pushup(x);
}
int merge(int x,int y,int l,int r){
if(!x||!y){
return x+y;
}
if(l==r){
sum[x]+=sum[y];
return x;
}
int mid=(l+r)>>;
ls[x]=merge(ls[x],ls[y],l,mid);
rs[x]=merge(rs[x],rs[y],mid+,r);
pushup(x);
return x;
}
void dfs(int x){
for(int i=first[x];i;i=nex[i]){
int y=to[i];
if(y==f[x][]) continue;
//root[x]=merge(root[x],root[y],ls[x],rs[x]); my wrong way
dfs(y);
root[x]=merge(root[x],root[y],,m);
}
if(sum[root[x]])ans[x]=num[posm[root[x]]];//num[posm[root[x]]] x wai yaojia root
}
int main(){
scanf("%d%d",&n,&m);
t=log2(n);
for(int i=;i<n;i++){
int a,b;
scanf("%d%d",&a,&b);
add(a,b);
add(b,a);
}
bfs();
for(int i=;i<=m;i++){
scanf("%d%d%d",&x[i],&yy[i],&zz[i]);
num[i]=zz[i];
}
sort(num+,num++m);
for(int i=;i<=m;i++){
zz[i]=lower_bound(num+,num+m/*m !n*/+,zz[i])-num;
int lca=Lca(x[i],yy[i]);
update(root[x[i]],zz[i],,,m);update(root[yy[i]],zz[i],,,m);update(root[lca],zz[i],-,,m);if(f[lca][])update(root[f[lca][]],zz[i],-,,m);
}
dfs();
for(int i=;i<=n;i++) printf("%d\n",ans[i]);
}
我还是太弱了,orzorz。
bzoj3307 雨天的尾巴题解及改题过程(线段树合并+lca+树上差分)的更多相关文章
- P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并 (树上差分+线段树合并)
显然的树上差分问题,最后要我们求每个点数量最多的物品,考虑对每个点建议线段树,查询子树时将线段树合并可以得到答案. 用动态开点的方式建立线段树,注意离散化. 1 #include<bits/st ...
- bzoj3307 雨天的尾巴 题解(线段树合并+树上差分)
Description N个点,形成一个树状结构.有M次发放,每次选择两个点x,y 对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.完成 所有发放后,每个点存放最多的是哪种物品. Input ...
- P4556 [Vani有约会]雨天的尾巴(线段树合并+lca)
P4556 [Vani有约会]雨天的尾巴 每个操作拆成4个进行树上差分,动态开点线段树维护每个点的操作. 离线处理完向上合并就好了 luogu倍增lca被卡了5分.....于是用rmq维护.... 常 ...
- bzoj 3307: 雨天的尾巴【树剖lca+树上差分+线段树合并】
这居然是我第一次写线段树合并--所以我居然在合并的时候加点结果WAWAWAMLEMLEMLE--!ro的时候居然直接指到la就行-- 树上差分,每个点建一棵动态开点线段树,然后统计答案的时候合并即可 ...
- 【HNOI2012】永无乡 题解(并查集+线段树合并)
题目链接 给定一张含$n$个点$m$条边的无向图,每个点有一个重要指数$a_i$.有两种操作:1.在$x$和$y$之间连一条边:2.求$x$所在连通块中重要程度第$k$小的点. ----------- ...
- 【BZOJ3307】雨天的尾巴 题解(树链剖分+树上差分)
题目链接 题目大意:给定一颗含有$n$个结点的树,每次选择两个结点$x$和$y$,对从$x$到$y$的路径上发放一带$z$类型的物品.问完成所有操作后每个结点发放最多的时哪种物品. 普通的树链剖分貌似 ...
- 【BZOJ3307】雨天的尾巴 线段树合并
[BZOJ3307]雨天的尾巴 Description N个点,形成一个树状结构.有M次发放,每次选择两个点x,y对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.完成所有发放后,每个点存放最多 ...
- [BZOJ3307] 雨天的尾巴(树上差分+线段树合并)
[BZOJ3307] 雨天的尾巴(树上差分+线段树合并) 题面 给出一棵N个点的树,M次操作在链上加上某一种类别的物品,完成所有操作后,要求询问每个点上最多物品的类型. N, M≤100000 分析 ...
- 雨天的尾巴(bzoj3307)(线段树合并+树上差分)
\(N\)个点,形成一个树状结构.有\(M\)次发放,每次选择两个点\(x,y\) 对于\(x\)到\(y\)的路径上(含\(x,y\))每个点发一袋\(Z\)类型的物品.完成 所有发放后,每个点存放 ...
随机推荐
- 使用iview ui库 [vue/no-parsing-error] Parsing error: x-invalid-end-tag报错
打开设置,搜索“vetur.validation.template”,设置完毕之后记得重启vscode 如果不行请使用下边方法 一. 问题日志 ✘ https://google.com/#q=vue% ...
- centos服务器之间相互挂载(samba)
前提:假设A服务器ip为:192.168.1.101 ,B服务器ip为:192.168.1.102现在要求把A服务器的/mnt/test 路径下的文件夹 共享到B服务器的/home/ceshi 下. ...
- 区间问题 codeforces 422c+hiho区间求差问
先给出一个经典的区间处理方法 对每个区间 我们对其起点用绿色标识 终点用蓝色标识 然后把所有的点离散在一个坐标轴上 如下图 这样做有什么意义呢.由于我们的区间可以离散的放在一条轴上面那么我们在枚举区 ...
- R语言学习笔记:读取前n行数据
常规读取 一般我们读取文件时都会读取全部的文件然后再进行操作,因为R是基于内存进行计算的. data <- read.table("C:\\Users\\Hider\\Desktop\ ...
- js之运算符(关系运算符)
关系运算符用于测试两个值之间的关系,根据关系是否存在而返回true或者是false.关系表达式总是返回一个布尔值. 具有如下8个关系运算符:大于(>),小于(<),小于等于(<=), ...
- Oracle的FIXED
今天发现一个有意思的问题,我们知道,在Oracle数据库中正常执行 select sysdate from dual 都可以返回当前主机的系统时间.正常修改系统时间,对应的查询结果也会变成修改后的系统 ...
- Windows 系统自动登录配置
1. open regedit 2. HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon 3.在打开的项右 ...
- (备忘)Java数据类型中String、Integer、int相互间的转换
1.Integer转换成int的方法 Integer i; int k = i.intValue();即Integer.intValue(); 2.int转换成Integer int i; Integ ...
- python网络编程:socket套接字
一.socket 二.TCP服务器 三.TCP客户端 四.UDP服务器 五.UDP客户端 六.聊天的客户端 七.聊天的服务器 一.socket """ 学习网络编程 其实 ...
- P_C_Brules
最小孔径10mil,最小线宽4mil,最小安全间距4mil.这个是一般厂家能做的.嘉立创为5mil. 1.xiankuan . 一般设为10mil.嘉立创多层板3.5mil,单双面5mil 电流的考量 ...