题目链接

\(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表)相关推荐

  1. CF1413F. Roads and Ramen(树的直径,线段树)

    CF1413F. Roads and Ramen Solution 感觉这个套路也见过许多次了?大概这种奇奇怪怪的树上最长路径的题都得往直径靠一靠. 大概有个结论是:存在一个最优路径使得其起始点和直径 ...

  2. 【用学校抄作业带你走进可持久化线段树(主席树)】可持久化线段树概念+全套模板+例题入门:[福利]可持久化线段树)

    我似乎很少写这种算法博客 可持久化线段树概念 概念介绍(类比帮助理解) 简单分析一下时间和空间复杂度(内容池) 模板 结构体变量 建树模板 单点修改模板 单点查询模板 区间修改模板(pushup) 区 ...

  3. [牛客网#35D 树的距离]离散化+线段树合并

    [牛客网#35D 树的距离]离散化+线段树合并 分类:Data Structure SegMent Tree Merge 1. 题目链接 [牛客网#35D 树的距离] 2. 题意描述 wyf非常喜欢树 ...

  4. 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 ...

  5. 2019南昌网络赛  I. Yukino With Subinterval 树状数组套线段树

    I. Yukino With Subinterval 题目链接: Problem Descripe Yukino has an array \(a_1, a_2 \cdots a_n\). As a ...

  6. 如何在vs中创建r树索引代码_线段树详解与实现

    此篇文章用于记录<玩转数据结构>课程的学习笔记 什么是线段树 线段树也被称为区间树,英文名为Segment Tree或者Interval tree,是一种高级的数据结构.这种数据结构更多出 ...

  7. 数据结构:树套树-替罪羊树套权值线段树

    BZOJ3065 本题是在BZOJ上的处女A,实在不应该拿这样一道题来开头 平衡树套线段树应该是树套树问题里比较难的一种了,当然我记得还有一个替罪羊树套Trie树的题,我是不信自己能写出来的. 外层的 ...

  8. 树状数组及线段树入门(SDNU1665-1668)

    目录 前言 树状数组 先导 单点修改区间查询 区间修改区间查询 线段树 先导 单点修改区间查询--递归形式 单点修改区间查询--非递归形式 区间修改区间查询--递归形式 区间修改区间查询--非递归形式 ...

  9. 势能线段树(吉司机线段树)专题

    势能线段树(吉司机线段树)专题 势能线段树在近期训练时遇到了好几次,但是由于本人太懒一直没补完,结果ICPC网络赛还真就出了一道势能线段树Orz--结果当然是没做出来--痛定思痛,这回把之前欠的一块儿 ...

最新文章

  1. [LeetCode题解] ZigZag Conversion
  2. 【Python】进制转换
  3. pandas将df赋值到另一个df_Python pandas将多级列标题df映射到另一个df
  4. python程序写诗_将Python诗歌与D结合起来
  5. Linux实现ICMP PING代码
  6. 未来,App就是一个人的全部
  7. 【Linux】15 张 Vim 速查表奉上,帮你提高 N 倍效率!
  8. Android 的 Recovery 模式分析
  9. 全球名校AI课程库(23)| Harvard哈佛 · 基于Python/JavaScript的Web编程课程『Web Programming with Python and JavaScript』
  10. linux 批量ping ip脚本,Linux下批量ping某个网段ip的脚本
  11. Avue表单select相关
  12. python批量新建文件夹_python批量创建文件夹
  13. i7 10510u相当于什么处理器
  14. 快速上手LaTex,书写美观学术论文
  15. 使用自定义注解实现接口参数校验
  16. Unity编写Shader内置各种矩阵和方法介绍
  17. linux irc 客户端,IRC 频道与客户端
  18. 【Unity】Unity在运行时崩溃了怎么办?别害怕,还有救!
  19. 苹果计算机怎么开科学,苹果手机怎样设置科学计算器?
  20. NVIDIA Jetson TX2 内核中添加 CP210x 串口驱动

热门文章

  1. console.log()与alert()的区别
  2. iOS学习 NSString常用技巧
  3. [ofbiz]设置任务计划(job),提示service_item已经传递
  4. 消息(6)——WCF,构建简单的WCF服务,MTOM编码
  5. ACRush 楼天城回忆录
  6. workerman php访问,workerman 配置域名访问 (本地)
  7. 如何判断两个平面相交_七年级下册相交线与平行线全章节复习
  8. java分页查询_面试官:数据量很大,分页查询很慢,有什么优化方案?
  9. VS2017无法启动
  10. mysql如何在一个表中插入数据的同时,更新另一个表的数据?