题意:

给出一个n个点的树,有m个事件;

每次是将x到y的路径上所有点都投放一个z的物品;

求最后每个点上那个物品最多;

n,m<=100000;

题解:

一道数据结构好题;

首先这道题问的是最终答案,那么把所有事件离线下来处理;

考虑如果树是一条链,那么对于这样一个序列问题怎么做?

将左端点x设置z数量加一,右端点y设为z数量-1;

然后用一个权值线段树去扫整个序列,查询最大值即为答案;

时间复杂度O((n+m)logm),空间复杂度O(mlogm);

十分优雅的做法,我们把它转移到树上;

树上的标记与序列大致相同,我们从叶节点向上扫整棵树;

那么在x,y结点标z数量加一,LCA(x,y),fa(LCA(x,y))设置z数量-1;

这似乎是树链问题转化很常用的手段之一?

然后我们一样的向上扫。。。两个点扫到了一个根!

现在要把这两个线段树合并起来;

如果直接启发式合并复杂度有点奇怪吧,不过权值线段树有更加优越的方法;

直接递归合并!

如果两个树有一个没结点,直接返回有结点的那个;

如果都有结点就递归计算,然后将计算出来的新结点返回,两个旧结点回收;

这个的复杂度是总体是O(nlogn)的(单次可能O(n)但是总体卡不住);

以下是PoPoQQQ大爷的精彩证明[鼓掌熊]:

我定义一棵线段树的势为节点个数

那么合并两棵线段树的代价为 两棵线段树的势和-新线段树的势

然后一开始所有线段树的势之和为O(nlogn)

一个线段树上有logn个节点

合并到最后有n个节点

所以最后的势能差是O(nlogn)

完事了

然后这题就能时间空间都是nlogn了

证明了复杂度可靠,那这题就置剩下实现的细节问题了;

别的倒是没什么,但是这题爆栈!爆栈!!!

然后改个宽搜就过了= =,真是够了;

代码:

#include<queue>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 110000
#define MEM 2000000
#define M 1000000000
#define lson l,mid,ls[no]
#define rson mid+1,r,rs[no]
using namespace std;
int fa[N][30],deep[N],size[N],ts[N],ans[N];
int next[N<<2],kind[N<<2],head[N],ce;
bool val[N<<2];
int ma[MEM],kma[MEM],ls[MEM],rs[MEM],root[N],tot;
int mp[MEM],top;
queue<int>q;
int newseg()
{if(top)    return mp[top--];return ++tot;
}
void delseg(int no)
{mp[++top]=no;ls[no]=0;rs[no]=0;ma[no]=0;
}
void Pushup(int no)
{if(ma[ls[no]]>=ma[rs[no]])ma[no]=ma[ls[no]],kma[no]=kma[ls[no]];elsema[no]=ma[rs[no]],kma[no]=kma[rs[no]];
}
void update(int l,int r,int &no,int k,int val)
{if(!no)    no=newseg();if(l==r)ma[no]+=val,kma[no]=l;else{int mid=l+r>>1;if(k<=mid)  update(lson,k,val);else     update(rson,k,val);Pushup(no);}if(!ma[no])kma[no]=0;
}
int merge(int l,int r,int nol,int nor)
{if(!nol||!nor) return nol+nor;int no=newseg();if(l==r){ma[no]=ma[nol]+ma[nor];kma[no]=l;}else{int mid=l+r>>1;ls[no]=merge(l,mid,ls[nol],ls[nor]);rs[no]=merge(mid+1,r,rs[nol],rs[nor]);Pushup(no);}delseg(nol),delseg(nor);return no;
}
namespace Graph
{int to[N<<1],next[N<<1],head[N],ce;int tq[N],st,en;void add(int x,int y){to[++ce]=y;next[ce]=head[x];head[x]=ce;}void bfs(){tq[st=en=1]=1;int i,x;while(st<=en){x=tq[st++];size[x]=1,deep[x]=deep[fa[x][0]]+1;for(i=1;fa[x][i-1];i++)fa[x][i]=fa[fa[x][i-1]][i-1];for(i=head[x];i;i=next[i]){if(to[i]!=fa[x][0]){fa[to[i]][0]=x;tq[++en]=to[i];}}}while(en){size[fa[tq[en]][0]]+=size[tq[en]];if(size[tq[en]]==1)q.push(tq[en]);en--;}}
};
int LCA(int x,int y)
{if(deep[x]<deep[y])swap(x,y);int k=20;while(deep[x]!=deep[y]){if(deep[fa[x][k]]>=deep[y])x=fa[x][k];k--;}if(x==y)  return x;k=20;while(k>=0){if(fa[x][k]!=fa[y][k])x=fa[x][k],y=fa[y][k];k--;}return fa[x][0];
}
void Insert(int x,int k,bool v)
{val[++ce]=v;kind[ce]=k;next[ce]=head[x];head[x]=ce;
}
int main()
{int n,m,i,j,k,l,x,y,z;scanf("%d%d",&n,&m);for(i=1;i<n;i++){scanf("%d%d",&x,&y);Graph::add(x,y);Graph::add(y,x);}Graph::bfs();for(i=1;i<=m;i++){scanf("%d%d%d",&x,&y,&z);l=LCA(x,y);Insert(x,z,1),Insert(y,z,1);Insert(l,z,0),Insert(fa[l][0],z,0);}while(!q.empty()){x=q.front(),q.pop();for(i=head[x];i;i=next[i])update(1,M,root[x],kind[i],val[i]?1:-1);ans[x]=kma[root[x]];root[fa[x][0]]=merge(1,M,root[x],root[fa[x][0]]);ts[fa[x][0]]+=size[x];if(ts[fa[x][0]]+1==size[fa[x][0]])q.push(fa[x][0]);}for(i=1;i<=n;i++)printf("%d\n",ans[i]);return 0;
}

bzoj-3307 雨天的尾巴相关推荐

  1. bzoj3307: 雨天的尾巴

    3307: 雨天的尾巴 Time Limit: 10 Sec  Memory Limit: 128 MB Submit: 479  Solved: 214 [Submit][Status][Discu ...

  2. bzoj3307 雨天的尾巴

    3307: 雨天的尾巴 Time Limit: 10 Sec  Memory Limit: 128 MB Submit: 258  Solved: 121 [Submit][Status][Discu ...

  3. 【线段树合并】解题报告:luogu P4556雨天的尾巴 (树上对点差分 + 动态开点 + 线段树合并)线段树合并模板离线/在线详解

    题目链接:雨天的尾巴 本题本身是一个非常简单的一道树上差分的模板题,但是由于变态的数据范围,我们直接用数组是存不下的(本来使用一颗普通的线段树直接维护最大值即可.但是本题的空间只有128MB,直接按照 ...

  4. P4556 雨天的尾巴

    题目背景 深绘里一直很讨厌雨天. 灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切. 虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连根拔起,以及田地 ...

  5. 【JZOJ3397】【luoguP4556】雨天的尾巴

    description 深绘里一直很讨厌雨天. 灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切. 虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连 ...

  6. [线段树][树上差分] Jzoj P3397 雨天的尾巴

    Description 深绘里一直很讨厌雨天. 灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切. 虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连 ...

  7. 线段树分裂与合并 ---- 树上差分 P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并

    题目链接 解题思路: 首先题目是对u,vu,vu,v这两条路径上面添加一个zzz,然后运用树上点的差分思想,对于分发路径u,vu,vu,v,我们在uuu上+1+1+1,在vvv上+1+1+1,在lca ...

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

    题目链接 Solution 树上差分+线段树合并. 在每个节点上维护一棵权值线段树. 然后如果需要修改 \(x,y\) 两点,则在 \(x\) 处和 \(y\) 处分别加上 \(1\) 的权值. 然后 ...

  9. 洛谷 - P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并(树上差分+线段树合并)

    题目链接:点击查看 题目大意:给出一棵树,再给出 m 次操作,每次操作会选择两个点 ( x , y ) ,使得这条路径上的所有点的种类 z 加一,最后问每个点的哪个种类出现的频率最高,若多个种类出现频 ...

  10. P4556,jzoj3397-[GDOI2014模拟]雨天的尾巴【树链剖分,线段树】

    正题 题目链接:https://www.luogu.org/problemnew/show/P4556 题目大意 nnn个点的一棵树,给出mmm个操作(x,y,z)(x,y,z)(x,y,z)表示将x ...

最新文章

  1. python使用opencv查找轮廓_(八)OpenCV-Python学习—轮廓查找,绘制和拟合
  2. OpenCV | 分水岭算法进行图像分割
  3. pyqt5实战之简陋的计算器
  4. 15.1 自定义分词器
  5. linux禁用页面拷贝粘贴,【Linux基础】VI命令模式下删除拷贝与粘贴
  6. 实验3-7 统计学生成绩 (15 分)
  7. cuda 实现sift gpu_超原版速度110倍,针对PyTorch的CPU到GPU张量迁移工具开源
  8. 手把手教你开发 MyBatis 插件
  9. php提取网页mp3,介绍三种提取网页中音乐URL网址的方法
  10. 使用Github搭建一个属于自己的网站
  11. 和讯博客知名博主《猎杀黑马》作者王宁签售会圆满结束
  12. Android组件化开发,组件间的Activity页面跳转。
  13. 25行代码爬取英雄联盟手游英雄皮肤图片
  14. trouble processing xxxx.class: Ill-advised or mistaken usage of a core class (java.* or javax.*)
  15. SemanticKITTI点云标注工具
  16. 实验一 简单计算器的实现(QT实现)
  17. Oracle创建表,id为自增序列
  18. 初学者的图片SEO指南 - 为搜索引擎优化图片
  19. 孙宏斌频变“脸谱” 乐视网该何去何从?
  20. ue4远程服务器xcode,UE4 使用Xcode真机调试的方法

热门文章

  1. ESA Sentinel-2 视角下的 US Aircraft Carrier
  2. java 地铁二号线站编号和站名的练习
  3. java中final是什么意思_java中final、finali、finally三者之间的区别是什么
  4. 腾讯会议下载文档的方式
  5. 可视对讲系统服务器,数字楼宇可视对讲系统
  6. 使用第三方WheelView制作日期选择器
  7. 史上最简单的JAVA实现PDF转HTML
  8. AddView和layoutParams总结
  9. 定位mp4文件没有声音或杂音问题的分析与总结
  10. 阿伏法机器人_智慧树知道答案机器人制作与创客综合能力实训课后作业答案