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

题面

给出一棵N个点的树,Q次询问一点编号在区间[l1,r1]内,另一点编号在区间[l2,r2]内的所有点对距离最大值。\(N, Q≤100000\)

分析

看到区间,我们应该想到用线段树维护,区间[l,r]存储编号在[l,r]内的点组成的一棵树的直径端点和长度

考虑如何合并区间。设两个区间的直径分别为(a,b) (c,d),则新区间的直径端点肯定也是a,b,c,d中的一个。(证明显然),那么新区间的直径就是max(dist(a,b),dist(a,c),dist(a,d),dist(b,c),dist(b,d),dist(c,d))

那么直接线段树维护就行了,pushup的时候按上面的那样合并。最后查询得到[l1,r1]内的直径(a,b),[l2,r2]内的直径(c,d) ,答案就是max(dist(a,c),dist(b,d),dist(b,c),dist(a,d))

如果用树上倍增求lca,时间复杂度为\(O(n\log^2n)\),改用dfs序+ST表求lca,查询只需要在ST表中求最值,是O(1)的,时间复杂度\(O(n\log n)\)

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 100000
#define maxlogn 25
using namespace std;
inline void qread(int &x) {
x=0;
int sign=1;
char c=getchar();
while(c<'0'||c>'9') {
if(c=='-') sign=-1;
c=getchar();
}
while(c>='0'&&c<='9') {
x=x*10+c-'0';
c=getchar();
}
x=x*sign;
}
inline void qprint(long long x) {
if(x<0) {
putchar('-');
qprint(-x);
} else if(x==0) {
putchar('0');
return;
} else {
if(x>=10) qprint(x/10);
putchar(x%10+'0');
}
} int n,m;
struct edge {
int from;
int to;
int next;
int len;
} E[maxn*2+5];
int head[maxn+5];
int esz=1;
void add_edge(int u,int v,int w) {
esz++;
E[esz].from=u;
E[esz].to=v;
E[esz].len=w;
E[esz].next=head[u];
head[u]=esz;
} int cnt=0;
long long dis[maxn+5];
int seq[maxn*2+5];
int first[maxn+5];
int deep[maxn*2+5];
void dfs(int x,int fa,int d) {
seq[++cnt]=x;
deep[cnt]=d;
first[x]=cnt;
for(int i=head[x]; i; i=E[i].next) {
int y=E[i].to;
if(y!=fa) {
dis[y]=dis[x]+E[i].len;
dfs(y,x,d+1);
seq[++cnt]=x;
deep[cnt]=d;
}
}
} struct ST {//ST表求lca
int log2[maxn*2+5];
int f[maxn*2+5][maxlogn+5];
void ini(int n) {
log2[0]=-1;
for(int i=1; i<=n; i++) log2[i]=log2[i>>1]+1;
for(int i=1; i<=n; i++) f[i][0]=i;
for(int j=1; (1<<j)<=n; j++) {
for(int i=1; i+(1<<j)-1<=n; i++) {
int x=f[i][j-1];
int y=f[i+(1<<(j-1))][j-1];
if(deep[x]<deep[y]) f[i][j]=x;
else f[i][j]=y;
}
}
}
int query(int l,int r) {
int k=log2[r-l+1];
int x=f[l][k];
int y=f[r-(1<<k)+1][k];
if(deep[x]<deep[y]) return x;
else return y;
}
} S;
inline int lca(int x,int y) {
x=first[x];
y=first[y];
if(x>y) swap(x,y);
return seq[S.query(x,y)];
}
inline long long dist(int x,int y) {
return dis[x]+dis[y]-2*dis[lca(x,y)];
} struct value {//直径的端点与长度
int u;
int v;
long long dis;
value() { }
value(int _u,int _v,int _dis) {
u=_u;
v=_v;
dis=_dis;
}
inline void cal(int x,int y) {
long long len=dist(x,y);
if(len>dis) {
dis=len;
u=x;
v=y;
}
}
friend value operator + (value p,value q) {//合并答案
if(p.u==0) return q;//注意一个为空的情况
if(q.u==0) return p;
int a=p.u,b=p.v,c=q.u,d=q.v;
value ans=value(0,0,0);
ans.cal(a,b);
ans.cal(a,c);
ans.cal(a,d);
ans.cal(b,c);
ans.cal(b,d);
ans.cal(c,d);
return ans;
}
}; struct segment_tree {
#define lson(x) (x<<1)
#define rson(x) (x<<1|1)
struct node {
int l;
int r;
value val;
} tree[maxn*4+5];
void push_up(int x) {
tree[x].val=tree[lson(x)].val+tree[rson(x)].val;//重载之后运算变得很简洁
}
void build(int l,int r,int pos) {
tree[pos].l=l;
tree[pos].r=r;
tree[pos].val=value(0,0,0);
if(l==r) {
tree[pos].val=value(l,l,0);
return;
}
int mid=(l+r)>>1;
build(l,mid,pos<<1);
build(mid+1,r,pos<<1|1);
push_up(pos);
}
value query(int L,int R,int pos) {
if(L<=tree[pos].l&&R>=tree[pos].r) {
return tree[pos].val;
}
int mid=(tree[pos].l+tree[pos].r)>>1;
value ans=value(0,0,0);
if(L<=mid) ans=ans+query(L,R,pos<<1);
if(R>mid) ans=ans+query(L,R,pos<<1|1);
return ans;
}
} T; int main() {
int u,v,w;
int l1,r1,l2,r2;
qread(n);
for(int i=1; i<n; i++) {
qread(u);
qread(v);
qread(w);
add_edge(u,v,w);
add_edge(v,u,w);
}
dfs(1,0,1);
S.ini(cnt);
T.build(1,n,1); qread(m);
for(int i=1; i<=m; i++) {
qread(l1);
qread(r1);
qread(l2);
qread(r2);
value ans1=T.query(l1,r1,1);
value ans2=T.query(l2,r2,1);
//ans=ans1+ans2
int a=ans1.u,b=ans1.v,c=ans2.u,d=ans2.v;//这里不能只选ans1或ans2,所以不能写ans1+ans2
qprint(max(max(dist(a,c),dist(b,d)),max(dist(a,d),dist(b,c))));
putchar('\n');
}
}

[51nod 1766]树上的最远点对 (树的直径+ST表求lca+线段树)的更多相关文章

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

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

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

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

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

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

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

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

  5. 【树形结构】51nod 1766 树上的最远点对

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

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

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

  7. 51nod 1206 Picture 矩形周长求并 | 线段树 扫描线

    51nod 1206 Picture 矩形周长求并 | 线段树 扫描线 #include <cstdio> #include <cmath> #include <cstr ...

  8. 计蒜客 38229.Distance on the tree-1.树链剖分(边权)+可持久化线段树(区间小于等于k的数的个数)+离散化+离线处理 or 2.树上第k大(主席树)+二分+离散化+在线查询 (The Preliminary Contest for ICPC China Nanchang National Invitational 南昌邀请赛网络赛)

    Distance on the tree DSM(Data Structure Master) once learned about tree when he was preparing for NO ...

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

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

随机推荐

  1. python去除字符串中间空格的方法

    1.使用字符串函数replace a = 'hello world' a.replace(' ', '') # 'helloworld' 2.使用字符串函数split a = ''.join(a.sp ...

  2. Linux架构之Rsync守护进程推和拉

    第三十三章 Rsync服务 33.1)Rsync基本概述 rsync是一款开源.快速.多功能.可实现全量及增量的本地或远程数据同步备份的优秀工具.rsync软件适用于Unix/linux/Window ...

  3. 为什么需要bootloader

    本文链接:https://blog.csdn.net/u012351051/article/details/50557899 受单片机和ARM7等小型CPU设备编程思维的影响,开始对嵌入式linux和 ...

  4. git log的个性化设置

    --date=(relative|local|default|iso|rfc|short|raw) Only takes effect for dates shown in human-readabl ...

  5. 启动模式:uefi, legacy,以及GRUB命令使用

    机器启动模式:uefi, legacy 设置入口:BIOS:boot mode 磁盘分区表格式: gpt uefi所使用(此种模式下,grub只能识别gpt格式的boot引导项) mbr legacy ...

  6. Django的下载和基本指令

    1.下载Django pip3  install  django     #不写版本号的话,默认使下载最新版的django pip3  install   django == 2.1.2    #指定 ...

  7. OPTIONS请求后台处理 跨域Filter

    import cn.hutool.http.Method; import org.springframework.web.filter.OncePerRequestFilter; import jav ...

  8. 06 IntelliJ IDEA构建多模块项目

  9. MapGIS IGServer for java

    但是安装完之后,服务里面没有找到igserver服务 IGServer SManager cxf 怎么会找不到类呢?是服务映射出了问题,所以目录找不到.所以怎么配置目录呢?是在xml还是环境变量,还是 ...

  10. 28 August

    单调队列复习. 投资 (invest) 给定一带符号整数数列,求长度为 \([s, e]\) 的子区间的和的最大值.(最大子段和) 降二维为一维,for循环枚举区间右端点.预处理前缀和,问题转化为找到 ...