题目内容

\(n\)个点被\(n−1\)条边连接成了一颗树,边有权值\(w_i\)。有\(q\)个询问,给出\([a,b]\)和\([c,d]\)两个区间,表示点的标号请你求出两个区间内各选一点之间的最大距离,即你需要求出

\[\max \{ \text{dis}(i,j)\vert i\in[a,b],j\in [c,d] \}
\]

数据范围

\(1≤n,q≤10^5,1≤w_i≤10^4\)

思路

好像其实是很好写的一道题(虽然考场我写的暴力orz)

先想树的直径的合并性质:

若\(<a,b>\),\(<c,d>\)分别为原来两棵树的直径,那么合并后的新直径必定为\(<a,b>\),\(<c,d>\),\(<a,c>\),\(<a,d>\),\(<b,c>\),\(<b,d>\)中的最大值。

那么对于本题的路径合并也是类似,可以通过反证法证明。

假设原来的直径为\(<a,b>\),设合并之后的直径不是以\(a,b\)中的为端点而是\(<c,d>\),那么可以得出\(\text{dis}(b,d)\)和\(\text{dis}(a,d)\)均要小于\(\text{dis}(c,d)\)。可以看出\(<e,d>\)是公共的,那么可以得出\(\text{dis}(b,e)\)和\(\text{dis}(a,e)\)均小于\(\text{dis}(c,e)\),那么你一开始求直径的时候就会选择\(<b,c>\)或\(<a,c>\)其中一条,而不是\(<a,b>\)。

那么关于这个性质我们就可以开一个线段树,内存最大路径的端点下标,通过以上的性质合并。需要注意的是倍增求\(\text{LCA}\)常数太大会导致超时,所以可以选择树剖或\(\text{ST}\)表求\(\text{LCA}\)。

然后这里提供一种重载运算符的写法,感觉比直接写好多if少很多(但是本人习惯扩行qwq看起来和正常写法差不多),然后本人过于蒻所以不会\(\text{ST}\)表求\(\text{LCA}\)所以写的树剖。

代码

#include <bits/stdc++.h>
#define lson (rt<<1)
#define rson (rt<<1|1)
using namespace std;
const int maxn=1e5+10;
int n,m; struct Edge{
int from,to,w,nxt;
}e[maxn<<1]; inline int read(){
int x=0,fopt=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')fopt=-1;
ch=getchar();
}
while(isdigit(ch)){
x=(x<<3)+(x<<1)+ch-48;
ch=getchar();
}
return x*fopt;
} int head[maxn],cnt;
inline void add(int u,int v,int w){
e[++cnt].from=u;
e[cnt].to=v;
e[cnt].w=w;
e[cnt].nxt=head[u];
head[u]=cnt;
} int fa[maxn],dep[maxn],siz[maxn],dis[maxn],son[maxn];
void dfs1(int u){
dep[u]=dep[fa[u]]+1;siz[u]=1;
for(register int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==fa[u])continue;
fa[v]=u;
dis[v]=dis[u]+e[i].w;
dfs1(v);
siz[u]+=siz[v];
if(!son[u]||siz[v]>siz[son[u]])son[u]=v;
}
} int top[maxn];
void dfs2(int u,int t){
top[u]=t;
for(register int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==fa[u])continue;
dfs2(v,v==son[u]?t:v);
}
} inline int lca(int u,int v){
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]])swap(u,v);
u=fa[top[u]];
}
return dep[u]<dep[v]?u:v;
} inline int Getdis(int u,int v){
return dis[u]+dis[v]-2*dis[lca(u,v)];
} struct Node{
int l,r;
friend inline Node operator +(const Node& A,const Node& B){
int p[4]={A.l,A.r,B.l,B.r};
int Max=-1;
Node res;
for(int i=0;i<4;i++)
for(int j=i+1;j<4;j++){
int w=Getdis(p[i],p[j]);
if(w>Max){
Max=w;
res=(Node){p[i],p[j]};
}
}
return res;
}
}tree[maxn<<2]; inline void pushup(int rt){
tree[rt]=tree[lson]+tree[rson];
} inline void Build(int rt,int l,int r){
if(l==r){
tree[rt].l=tree[rt].r=l;
return;
}
int mid=(l+r)>>1;
Build(lson,l,mid);
Build(rson,mid+1,r);
pushup(rt);
} inline Node query(int rt,int l,int r,int s,int t){
if(s<=l&&t>=r)
return tree[rt];
int mid=(l+r)>>1;
if(t<=mid)return query(lson,l,mid,s,t);
else if(s>=mid+1)return query(rson,mid+1,r,s,t);
else return query(lson,l,mid,s,mid)+query(rson,mid+1,r,mid+1,t);
} inline int Mymax(int a,int b,int c,int d){
return max(a,max(b,max(c,d)));
} int main(){
freopen("D.in","r",stdin);
freopen("D.out","w",stdout);
n=read();
for(register int i=1;i<n;i++){
int x=read(),y=read(),z=read();
add(x,y,z);
add(y,x,z);
} dfs1(1);
dfs2(1,1);
Build(1,1,n); m=read();
while(m--){
int a=read(),b=read(),c=read(),d=read();
Node x=query(1,1,n,a,b);
Node y=query(1,1,n,c,d);
int ans=Mymax(Getdis(x.l,y.l),Getdis(x.l,y.r),Getdis(x.r,y.l),Getdis(x.r,y.r));
printf("%d\n",ans);
}
return 0;
}

【树形结构】51nod 1766 树上的最远点对的更多相关文章

  1. 51nod 1766 树上的最远点对 | LCA ST表 线段树 树的直径

    51nod 1766 树上的最远点对 | LCA ST表 线段树 树的直径 题面 n个点被n-1条边连接成了一颗树,给出a~b和c~d两个区间,表示点的标号请你求出两个区间内各选一点之间的最大距离,即 ...

  2. [51nod 1766]树上的最远点对 (树的直径+ST表求lca+线段树)

    [51nod 1766]树上的最远点对 (树的直径+ST表求lca+线段树) 题面 给出一棵N个点的树,Q次询问一点编号在区间[l1,r1]内,另一点编号在区间[l2,r2]内的所有点对距离最大值.\ ...

  3. 51Nod 1766 树上的最远点对

    Description 一棵树,询问两个端点编号分别在在 \([a,b]\) 和 \([c,d]\) 两个区间中的最长链. Sol 线段树+ST表. 树上最长链可以合并,只需要合并两个区间最长链的两个 ...

  4. 51nod 1766 树上的最远点对——线段树

    n个点被n-1条边连接成了一颗树,给出a~b和c~d两个区间,表示点的标号请你求出两个区间内各选一点之间的最大距离,即你需要求出max{dis(i,j) |a<=i<=b,c<=j& ...

  5. 51nod 1766 树上的最远点对(线段树)

    像树的直径一样,两个集合的最长路也是由两个集合内部的最长路的两个端点组成的,于是我们知道了两个集合的最长路,枚举一下两两端点算出答案就可以合并了,所以就可以用线段树维护一个区间里的最长路了. #inc ...

  6. 51 nod 1766 树上的最远点对(线段树+lca)

    1766 树上的最远点对 基准时间限制:3 秒 空间限制:524288 KB 分值: 80 难度:5级算法题   n个点被n-1条边连接成了一颗树,给出a~b和c~d两个区间,表示点的标号请你求出两个 ...

  7. 【51nod】1766 树上的最远点对

    [题意]给定n个点的树,m次求[a,b]和[c,d]中各选出一个点的最大距离.abcd是标号区间,n,m<=10^5 [算法]LCA+树的直径理论+线段树 [题解] 树的直径性质:距离树上任意点 ...

  8. 51Nod.1766.树上最远点对(树的直径 RMQ 线段树/ST表)

    题目链接 \(Description\) 给定一棵树.每次询问给定\(a\sim b,c\sim d\)两个下标区间,从这两个区间中各取一个点,使得这两个点距离最远.输出最远距离. \(n,q\leq ...

  9. 51Nod1766 树上的最远点对

    1766 树上的最远点对 n个点被n-1条边连接成了一颗树,给出a~b和c~d两个区间,表示点的标号请你求出两个区间内各选一点之间的最大距离,即你需要求出max{dis(i,j) |a<=i&l ...

随机推荐

  1. Vue 下拉刷新及无限加载组件

    原文  https://github.com/wangdahoo/vue-scroller 主题 Vue.js Vue Scroller Vue Scroller is a foundational ...

  2. Aggressive cows(POJ 2456)

    原题如下: Aggressive cows Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 20524   Accepted: ...

  3. mysql1045问题解决

    输入mysql -u root -P 1202 -h localhost -p时报错mysql1045 解决方法:my.ini中加上skip-grant-tables后重启mysql服务即可解决

  4. 吴恩达《深度学习》-第五门课 序列模型(Sequence Models)-第三周 序列模型和注意力机制(Sequence models & Attention mechanism)-课程笔记

    第三周 序列模型和注意力机制(Sequence models & Attention mechanism) 3.1 序列结构的各种序列(Various sequence to sequence ...

  5. [LeetCode] 203. 移除链表元素(链表基本操作-删除)、876. 链表的中间结点(链表基本操作-找中间结点)

    题目 203. 移除链表元素 删除链表中等于给定值 val 的所有节点. 题解 删除结点:要注意虚拟头节点. 代码 class Solution { public ListNode removeEle ...

  6. [LeetCode]230. 二叉搜索树中第K小的元素(BST)(中序遍历)、530. 二叉搜索树的最小绝对差(BST)(中序遍历)

    题目230. 二叉搜索树中第K小的元素 给定一个二叉搜索树,编写一个函数 kthSmallest 来查找其中第 k 个最小的元素. 题解 中序遍历BST,得到有序序列,返回有序序列的k-1号元素. 代 ...

  7. J.U.C之ReentrantLock 可重入锁

    * A reentrant mutual exclusion {@link Lock} with the same basic * behavior and semantics as the impl ...

  8. 快速了解前端开发HTML的正确姿势

    摘要:web前端开发(也称为客户端开发)主要是通过html,CSS和JavaScript等前端技术,实现网站在客服端的正确显示及交互功能. 一.web标准介绍 web标准: w3c:万维网联盟组织,用 ...

  9. Powershell编程基础-001-基本语法

    变量的定义:$a="abc"         变量的引用也是echo $a 注释: 1.单行注释:单行注释是在每行的开头键入井号#的注释. #符号右边的所有内容都将被忽略,也可以在 ...

  10. 使用Navicat连接MySQL8.0版本报1251错误

    出现1251错误是因为,MySQL8.0版本改变了密码的验证规则caching_sha2_password,MySQL之前的版本验证规则是mysql_native_password,现在需要修改MyS ...