51Nod.1766.树上最远点对(树的直径 RMQ 线段树/ST表)
题目链接
\(Description\)
给定一棵树。每次询问给定\(a\sim b,c\sim d\)两个下标区间,从这两个区间中各取一个点,使得这两个点距离最远。输出最远距离。
\(n,q\leq10^5\)。
\(Solution\)
一个集合直径的两端点,在被划分为两个集合后一定是两个集合直径的四个端点中的两个。
即假设将\(S\)分为两个集合后,另外两个集合的直径的两端点分别为a,b和c,d,那么\(S\)集合的直径的两端点一定是a,b,c,d中的两个。(前提是边权非负)
证明类似树的直径。
所以信息可以合并,所以就可以线段树啦。而且没有修改,ST表就够啦。
原来是两个区间各选一点。。=-=
写namespace不想改了...有点丑不要介意。
Update:RMQ和ST表还能优化。
ST表:
//500ms 44,948KB
#include <cstdio>
#include <cctype>
#include <algorithm>
#define BIT 17//2^{17}=131072
//#define gc() getchar()
#define MAXIN 500000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=2e5+5;//2nchar IN[MAXIN],*SS=IN,*TT=IN;inline int read()
{int now=0;register char c=gc();for(;!isdigit(c);c=gc());for(;isdigit(c);now=now*10+c-'0',c=gc());return now;
}
namespace PRE
{int Enum,H[N>>1],nxt[N],to[N],len[N],dis[N>>1],pos[N>>1],Log2[N],st[N][BIT+1];inline void AE(int w,int u,int v){to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum, len[Enum]=w;to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum, len[Enum]=w;}inline int LCA_dis(int l,int r){if(l>r) std::swap(l,r);int k=Log2[r-l+1];return std::min(st[l][k],st[r-(1<<k)+1][k])<<1;
// return dis[ref[std::min(st[l][k],st[r-(1<<k)+1][k])]]<<1;}inline int Dis(int x,int y){return dis[x]+dis[y]-LCA_dis(pos[x],pos[y]);}void DFS(int x,int fa){static int tot=0;st[pos[x]=++tot][0]=dis[x];//边权为正的话可以直接用dis[x]for(int i=H[x],v; i; i=nxt[i])if((v=to[i])!=fa) dis[v]=dis[x]+len[i], DFS(v,x), st[++tot][0]=dis[x];}void Init_RMQ(const int n){for(int i=2; i<=n; ++i) Log2[i]=Log2[i>>1]+1;for(int j=1; j<=Log2[n]; ++j)for(int t=1<<j-1,i=n-t; i; --i)st[i][j]=std::min(st[i][j-1],st[i+t][j-1]);}
}
namespace SOL
{struct Node{int x,y;}A[N>>1][BIT];using PRE::Log2;Node Merge(const Node &a,const Node &b){int x=a.x,y=a.y,X=b.x,Y=b.y,tx=x,ty=y,tmx=PRE::Dis(x,y),tmp;if((tmp=PRE::Dis(X,Y))>tmx) tmx=tmp,tx=X,ty=Y;if((tmp=PRE::Dis(x,X))>tmx) tmx=tmp,tx=x,ty=X;if((tmp=PRE::Dis(x,Y))>tmx) tmx=tmp,tx=x,ty=Y;if((tmp=PRE::Dis(y,X))>tmx) tmx=tmp,tx=y,ty=X;if((tmp=PRE::Dis(y,Y))>tmx) tmx=tmp,tx=y,ty=Y;return (Node){tx,ty};}inline Node Query(int l,int r){int k=Log2[r-l+1];return Merge(A[l][k],A[r-(1<<k)+1][k]);}void Init_ST(const int n){for(int i=1; i<=n; ++i) A[i][0]=(Node){i,i};for(int j=1; j<=Log2[n]; ++j)for(int t=1<<j-1,i=n-t; i; --i)A[i][j]=Merge(A[i][j-1],A[i+t][j-1]);}void Solve(const int n){Init_ST(n);for(int Q=read(); Q--; ){int a=read(),b=read(),c=read(),d=read();Node X=Query(a,b),Y=Query(c,d);printf("%d\n",std::max(PRE::Dis(X.x,Y.x),std::max(PRE::Dis(X.x,Y.y),std::max(PRE::Dis(X.y,Y.x),PRE::Dis(X.y,Y.y)))));}}
}int main()
{int n=read();for(int i=1; i<n; ++i) PRE::AE(read(),read(),read());PRE::DFS(1,1), PRE::Init_RMQ(2*n-1), SOL::Solve(n);return 0;
}
线段树:
//671ms 45,244KB
#include <cstdio>
#include <cctype>
#include <algorithm>
#define BIT 17
#define gc() getchar()
#define MAXIN 100000
//#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=2e5+5;//2nchar IN[MAXIN],*SS=IN,*TT=IN;inline int read()
{int now=0;register char c=gc();for(;!isdigit(c);c=gc());for(;isdigit(c);now=now*10+c-'0',c=gc());return now;
}
namespace PRE
{int Enum,H[N>>1],nxt[N],to[N],len[N],dis[N>>1],pos[N>>1],Log2[N],st[N][BIT+1];inline void AE(int w,int u,int v){to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum, len[Enum]=w;to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum, len[Enum]=w;}inline int LCA_dis(int l,int r){if(l>r) std::swap(l,r);int k=Log2[r-l+1];return std::min(st[l][k],st[r-(1<<k)+1][k])<<1;
// return dis[ref[std::min(st[l][k],st[r-(1<<k)+1][k])]]<<1;}inline int Dis(int x,int y){return dis[x]+dis[y]-LCA_dis(pos[x],pos[y]);}void DFS(int x,int fa){static int tot=0;st[pos[x]=++tot][0]=dis[x];//边权为正的话可以直接用dis[x]for(int i=H[x],v; i; i=nxt[i])if((v=to[i])!=fa) dis[v]=dis[x]+len[i], DFS(v,x), st[++tot][0]=dis[x];}void Init_RMQ(const int n){for(int i=2; i<=n; ++i) Log2[i]=Log2[i>>1]+1;for(int j=1; j<=Log2[n]; ++j)for(int t=1<<j-1,i=n-t; i; --i)st[i][j]=std::min(st[i][j-1],st[i+t][j-1]);}
}
struct Segment_Tree
{#define ls rt<<1#define rs rt<<1|1#define lson l,m,rt<<1#define rson m+1,r,rt<<1|1#define S N<<1//2nint n,ansx,ansy,ansmx,X[S],Y[S],mxds[S];#undef Svoid Merge(int &x,int &y,int &mx,int X,int Y,int Mx){int tmp,tx=x,ty=y,tmx=mx;if(Mx>tmx) tmx=Mx,tx=X,ty=Y;if((tmp=PRE::Dis(x,X))>tmx) tmx=tmp,tx=x,ty=X;if((tmp=PRE::Dis(x,Y))>tmx) tmx=tmp,tx=x,ty=Y;if((tmp=PRE::Dis(y,X))>tmx) tmx=tmp,tx=y,ty=X;if((tmp=PRE::Dis(y,Y))>tmx) tmx=tmp,tx=y,ty=Y;x=tx, y=ty, mx=tmx;}inline void Update(int rt){int l=ls,r=rs;Merge(X[rt]=X[l],Y[rt]=Y[l],mxds[rt]=mxds[l],X[r],Y[r],mxds[r]);}void Build(int l,int r,int rt){if(l==r) {X[rt]=Y[rt]=l; return;}int m=l+r>>1; Build(lson), Build(rson), Update(rt);}void Query(int l,int r,int rt,int L,int R){if(L<=l && r<=R) {Merge(ansx,ansy,ansmx,X[rt],Y[rt],mxds[rt]); return;}int m=l+r>>1;if(L<=m) Query(lson,L,R);if(m<R) Query(rson,L,R);}void Solve(){int a=read(),b=read(),c=read(),d=read();ansx=a, ansy=a, ansmx=0;Query(1,n,1,a,b);int x1=ansx,y1=ansy; ansx=c, ansy=c, ansmx=0;Query(1,n,1,c,d);int x2=ansx,y2=ansy;printf("%d\n",std::max(PRE::Dis(x1,x2),std::max(PRE::Dis(x1,y2),std::max(PRE::Dis(y1,x2),PRE::Dis(y1,y2)))));}
}T;int main()
{int n=read();for(int i=1; i<n; ++i) PRE::AE(read(),read(),read());PRE::DFS(1,1), PRE::Init_RMQ(2*n-1);T.n=n, T.Build(1,n,1);for(int Q=read(); Q--; T.Solve());return 0;
}
转载于:https://www.cnblogs.com/SovietPower/p/10090344.html
51Nod.1766.树上最远点对(树的直径 RMQ 线段树/ST表)相关推荐
- CF1413F. Roads and Ramen(树的直径,线段树)
CF1413F. Roads and Ramen Solution 感觉这个套路也见过许多次了?大概这种奇奇怪怪的树上最长路径的题都得往直径靠一靠. 大概有个结论是:存在一个最优路径使得其起始点和直径 ...
- 【用学校抄作业带你走进可持久化线段树(主席树)】可持久化线段树概念+全套模板+例题入门:[福利]可持久化线段树)
我似乎很少写这种算法博客 可持久化线段树概念 概念介绍(类比帮助理解) 简单分析一下时间和空间复杂度(内容池) 模板 结构体变量 建树模板 单点修改模板 单点查询模板 区间修改模板(pushup) 区 ...
- [牛客网#35D 树的距离]离散化+线段树合并
[牛客网#35D 树的距离]离散化+线段树合并 分类:Data Structure SegMent Tree Merge 1. 题目链接 [牛客网#35D 树的距离] 2. 题意描述 wyf非常喜欢树 ...
- BZOJ.4553.[HEOI2016TJOI2016]序列(DP 树状数组套线段树/二维线段树(MLE) 动态开点)
题目链接:BZOJ 洛谷 \(O(n^2)\)DP很好写,对于当前的i从之前满足条件的j中选一个最大值,\(dp[i]=d[j]+1\) for(int j=1; j<i; ++j)if(a[j ...
- 2019南昌网络赛 I. Yukino With Subinterval 树状数组套线段树
I. Yukino With Subinterval 题目链接: Problem Descripe Yukino has an array \(a_1, a_2 \cdots a_n\). As a ...
- 如何在vs中创建r树索引代码_线段树详解与实现
此篇文章用于记录<玩转数据结构>课程的学习笔记 什么是线段树 线段树也被称为区间树,英文名为Segment Tree或者Interval tree,是一种高级的数据结构.这种数据结构更多出 ...
- 数据结构:树套树-替罪羊树套权值线段树
BZOJ3065 本题是在BZOJ上的处女A,实在不应该拿这样一道题来开头 平衡树套线段树应该是树套树问题里比较难的一种了,当然我记得还有一个替罪羊树套Trie树的题,我是不信自己能写出来的. 外层的 ...
- 树状数组及线段树入门(SDNU1665-1668)
目录 前言 树状数组 先导 单点修改区间查询 区间修改区间查询 线段树 先导 单点修改区间查询--递归形式 单点修改区间查询--非递归形式 区间修改区间查询--递归形式 区间修改区间查询--非递归形式 ...
- 势能线段树(吉司机线段树)专题
势能线段树(吉司机线段树)专题 势能线段树在近期训练时遇到了好几次,但是由于本人太懒一直没补完,结果ICPC网络赛还真就出了一道势能线段树Orz--结果当然是没做出来--痛定思痛,这回把之前欠的一块儿 ...
最新文章
- [LeetCode题解] ZigZag Conversion
- 【Python】进制转换
- pandas将df赋值到另一个df_Python pandas将多级列标题df映射到另一个df
- python程序写诗_将Python诗歌与D结合起来
- Linux实现ICMP PING代码
- 未来,App就是一个人的全部
- 【Linux】15 张 Vim 速查表奉上,帮你提高 N 倍效率!
- Android 的 Recovery 模式分析
- 全球名校AI课程库(23)| Harvard哈佛 · 基于Python/JavaScript的Web编程课程『Web Programming with Python and JavaScript』
- linux 批量ping ip脚本,Linux下批量ping某个网段ip的脚本
- Avue表单select相关
- python批量新建文件夹_python批量创建文件夹
- i7 10510u相当于什么处理器
- 快速上手LaTex,书写美观学术论文
- 使用自定义注解实现接口参数校验
- Unity编写Shader内置各种矩阵和方法介绍
- linux irc 客户端,IRC 频道与客户端
- 【Unity】Unity在运行时崩溃了怎么办?别害怕,还有救!
- 苹果计算机怎么开科学,苹果手机怎样设置科学计算器?
- NVIDIA Jetson TX2 内核中添加 CP210x 串口驱动
热门文章
- console.log()与alert()的区别
- iOS学习 NSString常用技巧
- [ofbiz]设置任务计划(job),提示service_item已经传递
- 消息(6)——WCF,构建简单的WCF服务,MTOM编码
- ACRush 楼天城回忆录
- workerman php访问,workerman 配置域名访问 (本地)
- 如何判断两个平面相交_七年级下册相交线与平行线全章节复习
- java分页查询_面试官:数据量很大,分页查询很慢,有什么优化方案?
- VS2017无法启动
- mysql如何在一个表中插入数据的同时,更新另一个表的数据?