[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. smarty之缓存机制

    当smarty开启缓存时,当tpl文件内容改变时,则缓存页面会重新生成 test.php: <?php date_default_timezone_set('asia/shanghai'); h ...

  2. mysql 导出表中数据为excel的xls格式文件

    需求: 利用mysql客户端导出数据库中数据,以便进行分析,统计. 解决命令: 在windos命令行(linux同理)下,用如下命令即可: mysql -hlocalhost -uroot -ppas ...

  3. windows下数字以2进制打印

    #include <cstdlib> _itoa(num, buf, 2); 打印buf既是二进制

  4. Java List 类型转换

    java的类型转换让人忍不住在开始编码前骂一声  FUCK! 1  子类转父类 up List<SubClass> subs = ...; List<? extends BaseCl ...

  5. HTML5 Canvas(实战:绘制饼图2 Tooltip)

    继上一篇HTML5 Canvas(实战:绘制饼图)之后,笔者研究了一下如何给饼图加鼠标停留时显示的提示框. Plot对象 在开始Coding之前,笔者能够想到的最easy的方式,就是给饼图的每一个区域 ...

  6. JS中判断一个数组是否有相同数据的

    页面中有多个<select> $("select").each(function(){ str.push($(this).val());}); // join() 方法 ...

  7. linux运维、架构之路-MySQL主从复制

    一.MySQL主从复制原理图  MySQL主从复制原理:实现主从复制原理是三个线程完成的,主的I/O线程,备的I/O线程与SQL线程 1.首先主库db01需要开启binlog.授权一个replicat ...

  8. php implode()函数 语法

    php implode()函数 语法 作用:把数组元素组合为字符串惠州大理石平板 语法:implode(separator,array) 参数: 参数 描述 separator     可选.规定数组 ...

  9. CF613D Kingdom and its Cities 虚树 + 树形DP

    Code: #include<bits/stdc++.h> #define ll long long #define maxn 300003 #define RG register usi ...

  10. 小陈现有2个任务A,B要完成,每个任务分别有若干步骤如下 一道网上没啥题解的难题(至少我是这么觉得的)

    小陈现有2个任务A,B要完成,每个任务分别有若干步骤如下:A=a1->a2->a3,B=b1->b2->b3->b4->b5.在任何时候,小陈只能专心做某个任务的一 ...