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;//2n
char 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;//2n
char 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//2n
int n,ansx,ansy,ansmx,X[S],Y[S],mxds[S];
#undef S
void 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;
}
51Nod.1766.树上最远点对(树的直径 RMQ 线段树/ST表)的更多相关文章
- 51nod 1766 树上的最远点对 | LCA ST表 线段树 树的直径
51nod 1766 树上的最远点对 | LCA ST表 线段树 树的直径 题面 n个点被n-1条边连接成了一颗树,给出a~b和c~d两个区间,表示点的标号请你求出两个区间内各选一点之间的最大距离,即 ...
- [51nod 1766]树上的最远点对 (树的直径+ST表求lca+线段树)
[51nod 1766]树上的最远点对 (树的直径+ST表求lca+线段树) 题面 给出一棵N个点的树,Q次询问一点编号在区间[l1,r1]内,另一点编号在区间[l2,r2]内的所有点对距离最大值.\ ...
- [HNOI2016]树(可持久化线段树+树上倍增)
[HNOI2016]树(可持久化线段树+树上倍增) 题面 给出一棵n个点的模板树和大树,根为1,初始的时候大树和模板树相同.接下来操作m次,每次从模板树里取出一棵子树,把它作为新树里节点y的儿子.操作 ...
- 【bzoj3065】带插入区间K小值 替罪羊树套权值线段树
题目描述 从前有n只跳蚤排成一行做早操,每只跳蚤都有自己的一个弹跳力a[i].跳蚤国王看着这些跳蚤国欣欣向荣的情景,感到非常高兴.这时跳蚤国王决定理性愉悦一下,查询区间k小值.他每次向它的随从伏特提出 ...
- bzoj 1036 [ZJOI2008]树的统计Count(树链剖分,线段树)
1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 10677 Solved: 4313[Submit ...
- 【BZOJ3196】二逼平衡树(树状数组,线段树)
[BZOJ3196]二逼平衡树(树状数组,线段树) 题面 BZOJ题面 题解 如果不存在区间修改操作: 搞一个权值线段树 区间第K大--->直接在线段树上二分 某个数第几大--->查询一下 ...
- BZOJ 1901 Zju2112 Dynamic Rankings 树状数组套线段树
题意概述:带修改求区间第k大. 分析: 我们知道不带修改的时候直接上主席树就可以了对吧?两个版本号里面的节点一起走在线段树上二分,复杂度是O((N+M)logN). 然而这里可以修改,主席树显然是凉了 ...
- st表、树状数组与线段树 笔记与思路整理
已更新(2/3):st表.树状数组 st表.树状数组与线段树是三种比较高级的数据结构,大多数操作时间复杂度为O(log n),用来处理一些RMQ问题或类似的数列区间处理问题. 一.ST表(Sparse ...
- 【洛谷5439】【XR-2】永恒(树链剖分,线段树)
[洛谷5439][XR-2]永恒(树链剖分,线段树) 题面 洛谷 题解 首先两个点的\(LCP\)就是\(Trie\)树上的\(LCA\)的深度. 考虑一对点的贡献,如果这两个点不具有祖先关系,那么这 ...
随机推荐
- IP的计算
IP的计算 时间限制: 1 Sec 内存限制: 32 MB 位无符号整数来表示,一般用点分方式来显示,点将IP地址分成4个部分,每个部分为8位,表示成一个无符号整数(因此不需要用正号出现),如192 ...
- 判断三次URL可用性脚本
#!/bin/bash check_url() { HTTP_CODE=$(curl -o /dev/ -s -) ];then continue fi } URL_LIST="www.ba ...
- Android播放功能的实现
Android播放语言功能的实现 需要用到的类文件TextToSpeech,此类可以实现播放语言的功能 支持播放的语言1.英语 2.法语 3.德语 4.意语 5.西班牙语 1.实例化此类,添加上下文对 ...
- labelme连续将文件夹中的json文件进行可视化的指令
for /r C:\Users\Fourmi\Desktop\ZP0 %i in (*.json) do labelme_json_to_dataset %i
- 论文阅读笔记二十八:You Only Look Once: Unified,Real-Time Object Detection(YOLO v1 CVPR2015)
论文源址:https://arxiv.org/abs/1506.02640 tensorflow代码:https://github.com/nilboy/tensorflow-yolo 摘要 该文提出 ...
- MVC开发中的常见错误-02-在应用程序配置文件中找不到名为“OAEntities”的连接字符串。
在应用程序配置文件中找不到名为“OAEntities”的连接字符串. 分析原因:由于Model类是数据库实体模型,通过从数据库中引用的方式添加实体,所以会自动产生一个数据库连接字符串,而程序运行到此, ...
- 从零开始学C#——数据类型(三)
C#数据类型 在C#中,变量分为以下几种类型: 值类型 引用类型 指针类型 值类型 值类型变量可以直接分配给一个值,他们是从类System.ValucTpyc中派生. 值类型直接包含数据,比如int. ...
- mysql 检查一个字符串是不是身份证号
)CHARSET utf8) ) BEGIN DECLARE flag BOOL DEFAULT FALSE; AND number REGEXP CONCAT('^(([1][1-5])|([2][ ...
- 3.ELK 之elasticsearch CRUD
名词介绍 index: type: document: 数据类型: 索引(index)创建示例 . type的创建(7.x之后)将会去掉该内容,点我看为什么? 参考脚本: mapping使用 其他 ...
- NEST 之旅 · 开启
NEST 之旅 · 开启 Version:5.x 英文原文地址:Getting started 说实话,这篇文章翻译的很糟糕.但是我的能力有限,目前只有这个水平,还望各位多指导. NEST 是 Ela ...