题目描述

首先村落里的一共有n座房屋,并形成一个树状结构。然后救济粮分m次发放,每次选择两个房屋(x,y),然后对于x到y的路径上(含x和y)每座房子里发放一袋z类型的救济粮。
然后深绘里想知道,当所有的救济粮发放完毕后,每座房子里存放的最多的是哪种救济粮。

说明

对于20%的数据,1 <= n, m <= 100
对于50%的数据,1 <= n, m <= 2000
对于100%的数据,1 <= n, m <= 100000, 1 <= a, b, x, y <= n, 1 <= z <= 100000
空间131MB

题解

又是树上路径问题。

要支持路径修改,并且是每个点是一个桶加入一个数。

要支持最后的统一单点查询。

树链剖分的话,显然对于桶的处理,怕不是要树套树了。总之打标记是一个很麻烦的事情。

其实最后只要单点查询。

所以,我们还可以树上差分。

对于一般的树上差分,只是每个点有一个点权。

但是,这个题每个点必须有一个桶。

而且桶还得快速合并,并且支持查询出现最多的数

桶还不能开满,因为会MLE

所以,每个点开一个动态开点权值线段树即可。

每次差分4个点,x,y,lca,fa[lca]

最后,要像一般的树上差分一样dfs一遍

不同的是,这个是线段树合并。

merge函数(自己yy的):

int merge(int x,int y){
if(!x||!y) return x|y;
if(!t[x].ls&&!t[y].ls&&!t[x].rs&&!t[y].rs){
t[x].mx+=t[y].mx;
}
else{
t[x].ls=merge(t[x].ls,t[y].ls);
t[x].rs=merge(t[x].rs,t[y].rs);
pushup(x);
}
return x;
}

或者更普遍的:

int merge(int x,int y,int l,int r){
if(!x||!y) return x|y;
if(l==r){
t[x].mx+=t[y].mx;
}
else{
t[x].ls=merge(t[x].ls,t[y].ls,l,mid);
t[x].rs=merge(t[x].rs,t[y].rs,mid+,r);
pushup(x);
}
return x;
}

总之就是叶子判断注意,必须暴力合并。

复杂度?

时间:本质就是merge函数启动次数,启动一次可以认为常数级别。

成功merge一次,会删掉一个节点。如果不成功,直接return x|y也不会往下走了。

开始有mlogn节点。所以复杂度最多mlogn

空间:动态开点是最多mlogn*4,因为一次差分四点更新嘛。

而之后的合并又不需要新的内存。

可以赌一把,为了不MLE,开60*m也可以过。。。

(这个题不能内存回收,否则还可以节省的。)

bug:

1.应该rt[x]=merge(rt[x],rt[y],1,n)  开始 没有rt[x]=返回值?dev luogu都没报错。。。

2.线段树合并函数开始没有对叶子暴力处理。。。(毕竟还是第一次写)

3.还有,线段树维护的是mx最大数个数,id最大数的值。

如果t[rt[x]]的mx>0才行。因为我的程序只要差分有关,就把id记上了,虽然可能mx是-1。。。

代码:

#include<bits/stdc++.h>
#define mid ((l+r)>>1)
using namespace std;
const int N=+;
const int U=;
int n,m;
struct haha{
int nxt,to;
}e[*N];
int hd[N],cnt;
void add(int x,int y){
e[++cnt].nxt=hd[x];
e[cnt].to=y;hd[x]=cnt;
}
int fa[N][];
int dep[N];
int ans[N];
void dfs(int x,int d){//no fa
dep[x]=d;
for(int i=hd[x];i;i=e[i].nxt){
int y=e[i].to;if(y==fa[x][]) continue;
fa[y][]=x;dfs(y,d+);
}
}
int lca(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
for(int j=;j>=;j--){
if(dep[fa[x][j]]>=dep[y]) x=fa[x][j];
}
if(x==y) return x;
for(int j=;j>=;j--){
if(fa[x][j]!=fa[y][j]) x=fa[x][j],y=fa[y][j];
}return fa[x][];
}
struct node{
int mx,id;
int ls,rs;
}t[N*];
int tot;
int rt[N];
void pushup(int x){
if(t[x].ls&&t[x].rs){
t[x].mx=max(t[t[x].ls].mx,t[t[x].rs].mx);
t[x].id=t[t[x].ls].mx>=t[t[x].rs].mx?t[t[x].ls].id:t[t[x].rs].id;
}
else if(t[x].ls){
t[x].mx=t[t[x].ls].mx;
t[x].id=t[t[x].ls].id;
}
else if(t[x].rs){
t[x].mx=t[t[x].rs].mx;
t[x].id=t[t[x].rs].id;
}
}
void upda(int &x,int l,int r,int p,int c){ if(!x) x=++tot;
//cout<<x<<" "<<l<<" "<<r<<" : "<<p<<" "<<c<<endl;
if(l==r) {
t[x].id=l,t[x].mx+=c;
return;
}
if(p<=mid) upda(t[x].ls,l,mid,p,c);
else upda(t[x].rs,mid+,r,p,c);
pushup(x);
}
int merge(int x,int y,int l,int r){
if(!x||!y) return x|y;
if(l==r){
t[x].mx+=t[y].mx;
}
else{
t[x].ls=merge(t[x].ls,t[y].ls,l,mid);
t[x].rs=merge(t[x].rs,t[y].rs,mid+,r);
pushup(x);
}
return x;
}
void sol(int x){//no fa
//cout<<" solving "<<x<<endl;
for(int i=hd[x];i;i=e[i].nxt){
int y=e[i].to;
if(y==fa[x][]) continue;
sol(y);
//cout<<" rtx rty "<<rt[x]<<" "<<rt[y]<<endl;
rt[x]=merge(rt[x],rt[y],,U);
//cout<<t[rt[x]].mx<<" "<<t[rt[x]].id<<endl;
}
if(t[rt[x]].mx>)ans[x]=t[rt[x]].id;
}
int main()
{
scanf("%d%d",&n,&m);int x,y;
for(int i=;i<=n-;i++){
scanf("%d%d",&x,&y);add(x,y);add(y,x);
}dfs(,);
dep[]=-;
for(int j=;j<=;j++)
for(int i=;i<=n;i++)
fa[i][j]=fa[fa[i][j-]][j-];
int z;
while(m--){
scanf("%d%d%d",&x,&y,&z);
int anc=lca(x,y);
//cout<<" anc "<<anc<<" "<<fa[anc][0]<<endl;
upda(rt[x],,U,z,);
upda(rt[y],,U,z,);
upda(rt[anc],,U,z,-);
if(fa[anc][])upda(rt[fa[anc][]],,U,z,-); }
/*for(int i=1;i<=n;i++){
cout<<" rt "<<i<<" "<<rt[i]<<" : "<<t[rt[i]].mx<<" "<<t[rt[i]].id<<endl;
}*/
sol();
for(int i=;i<=n;i++){
printf("%d\n",ans[i]);
}return ;
}

总结:

比较裸的树上差分

然后比较裸的线段树合并。

就是把树上差分的东西从值变成了物品。

动态开点线段树+合并就应运而生了。

[Vani有约会]雨天的尾巴——树上差分+动态开点线段树合并的更多相关文章

  1. [Vani有约会]雨天的尾巴 线段树合并

    [Vani有约会]雨天的尾巴 LG传送门 线段树合并入门好题. 先别急着上线段树合并,考虑一下这题的暴力.一看就是树上差分,对于每一个节点统计每种救济粮的数量,再一遍dfs把差分的结果统计成答案.如果 ...

  2. P4556 [Vani有约会]雨天的尾巴(线段树合并+lca)

    P4556 [Vani有约会]雨天的尾巴 每个操作拆成4个进行树上差分,动态开点线段树维护每个点的操作. 离线处理完向上合并就好了 luogu倍增lca被卡了5分.....于是用rmq维护.... 常 ...

  3. 「Luogu4556」Vani有约会-雨天的尾巴

    「Luogu4556」Vani有约会-雨天的尾巴 传送门 很显然可以考虑树上差分+桶,每次更新一条链就是把这条链上的点在桶对应位置打上 \(1\) 的标记, 最后对每个点取桶中非零值的位置作为答案即可 ...

  4. [题解] P4556 [Vani有约会]雨天的尾巴

    [题解] P4556 [Vani有约会]雨天的尾巴 ·题目大意 给定一棵树,有m次修改操作,每次修改 \(( x\) \(y\) \(z )\) 表示 \((x,y)\) 之间的路径上数值 \(z\) ...

  5. 洛谷 P4556 [Vani有约会]雨天的尾巴 解题报告

    P4556 [Vani有约会]雨天的尾巴 题目背景 深绘里一直很讨厌雨天. 灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切. 虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒 ...

  6. P4556 [Vani有约会]雨天的尾巴 (线段树合并)

    P4556 [Vani有约会]雨天的尾巴 题意: 首先村落里的一共有n座房屋,并形成一个树状结构.然后救济粮分m次发放,每次选择两个房屋(x,y),然后对于x到y的路径上(含x和y)每座房子里发放一袋 ...

  7. P4556 [Vani有约会]雨天的尾巴

    目录 思路 优化 过程中的问题/疑问 错误 代码 思路 每个节点维护一课线段树(当然是动态开点) 线段树的作用是统计这个节点有多少种粮食型号,以及最多的粮食型号 然后树上差分,u和v点 +1,lca( ...

  8. P4556 [Vani有约会]雨天的尾巴(线段树合并)

    传送门 一道线段树合并 首先不难看出树上差分 我们把每一次修改拆成四个,在\(u,v\)分别放上一个,在\(lca\)和\(fa[lca]\)各减去一个,那么只要统计一下子树里的总数即可 然而问题就在 ...

  9. BZOJ 3307 雨天的尾巴 (树上差分+线段树合并)

    题目大意:给你一棵树,树上一共n个节点,共m次操作,每次操作给一条链上的所有节点分配一个权值,求所有节点被分配到所有的权值里,出现次数最多的权值是多少,如果出现次数相同就输出最小的. (我辣鸡bzoj ...

随机推荐

  1. SQL中读取Excel 以及 bpc语言

    --开启导入功能 reconfigure reconfigure --允许在进程中使用ACE.OLEDB.12 --允许动态参数 EXEC master.dbo.sp_MSset_oledb_prop ...

  2. 点击小图查看大图jQuery插件FancyBox魔幻灯箱

    今日发现一个不错的JQuery插件FancyBox,也许早就有这个插件了,但是没名字,我就暂且叫他魔幻灯箱吧,采用Mac系统的样式.网传主要有以下功能:■弹出的窗口有很漂亮的阴影效果.■关联的对象(就 ...

  3. 为什么找程序员一定要看他的 GitHub

    听说 最开始听到这句话是某知名互联网公司大牛告诉我的,我很不以为然,不过迫于他是"leader"我也注册了一个 highsea (广告 0.0):当然我可懒得 push 更别提 c ...

  4. oracle数据update后怎么恢复到以前的数据

    http://blog.csdn.net/itdada/article/details/52746392

  5. 允许使用root远程ssh登录(Ubuntu 16.04)

    今天装了ubuntu16和17,发现还是ubuntu16看着顺眼,所以以后决定用ubuntu16, 然后想换语言发现更新失败,所以想换成中国的源,但是vm里面复制粘贴不了,所以想用secureCRT连 ...

  6. 对视频播放软件KMplayer的评价

    刚进入大一的时候接触到了KMplayer,由于当时收集了不同格式的电影视频却没有合适的播放器播放出来,后来在网上知道了所谓的万能播放器的KMplayer,下载用了过后,才知道这的确是一款万能播放器. ...

  7. TeamWork#2,Week 5,Our Measurement of Contribution to the Team

    经过了今天下午将近两个小时的激烈讨论,我们最终确定了我们的团队贡献分的分配方式,这种方式是我们团队都能接受的. 我们的分配方式一定程度上借鉴了valve公司的队友评估原则,但是又不单单是这样.我们的分 ...

  8. Scrum Meeting 10.22

    Scrum Meeting No.2 今天的主要任务是配置安卓开发环境,并运行上一届的项目. 主流的安卓开发环境有eclipse+ADT+SDK和android studio两种.两种环境的文件架构似 ...

  9. 《Spring1之第四次站立会议》

    <第四次站立会议> 昨天:我把小组成员找到的写关于登录界面的代码加到了我的项目工程里,并对它有了一定的了解,已经能够编译运行了,得到了登陆的界面: 今天:试着做了一下主框架里的在线人数的显 ...

  10. ubuntu16.04+cuda8.0+caffe

    =========== 如果出现nvidia-smi failed to communicate with nvidia driver,循环登录情况,则: sudo apt-get remove -- ...