[51nod] 1766树上的最远点对 树的直径 树剖LCA+ST表静态查询
Posted 昵称很长很长真是太好了
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[51nod] 1766树上的最远点对 树的直径 树剖LCA+ST表静态查询相关的知识,希望对你有一定的参考价值。
题意:
给你一棵带权树,q次查询,每次给出两个区间,
[
l
1
,
r
1
]
[
l
2
,
r
2
]
[l_1,r_1] [l_2,r_2]
[l1,r1][l2,r2]从这两个区间中分别选择两个数字,使得这两个点的距离最大,这个最大的距离是多少。
题解:
树的直径性质:距离树上任意点最远的点一定是直径的一端。此结论在点集中依然试用。
那么答案路径的两端一定是
[
l
1
,
r
1
]
[l_1,r_1]
[l1,r1]直径的一端和
[
l
2
,
r
2
]
[l_2,r_2]
[l2,r2]直径的一端的连线。
那么我们只要求出
[
l
1
,
r
1
]
[l_1,r_1]
[l1,r1]的直径(两个端点+直径长度),
[
l
2
,
r
2
]
[l_2,r_2]
[l2,r2]直径(两个端点+直径长度)
假设
[
l
1
,
r
1
]
[l_1,r_1]
[l1,r1]的直径端点为a,b,假设
[
l
2
,
r
2
]
[l_2,r_2]
[l2,r2]的直径端点为c,d,那么答案即为
m
a
x
{
a
到
c
,
a
到
d
,
b
到
c
,
b
到
d
}
max \\{a到c,a到d,b到c,b到d \\}
max{a到c,a到d,b到c,b到d} 由上面的结论得出。
那么如何快速求出
[
l
1
,
r
1
]
[l_1,r_1]
[l1,r1]的直径和
[
l
2
,
r
2
]
[l_2,r_2]
[l2,r2]的直径。
既然我们已经有如上的结论了那么对于一个连续的区间,我们可以通过线段树来求,对于区间的网上递归合并。
因为此题为静态查询,那么线段树可以替换成ST表,
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)预处理后
O
(
1
)
O(1)
O(1)查询即可
代码:
#include<bits/stdc++.h>
#define endl '\\n'
using namespace std;
const int maxn=2e5+10;
struct node{
int to,w,next;
}edge[maxn];
int head[maxn],cnt;
void add(int u,int v,int w){
edge[cnt].to=v;
edge[cnt].w=w;
edge[cnt].next=head[u];
head[u]=cnt++;
}
int fa[maxn],dis[maxn],siz[maxn],son[maxn],dep[maxn];
void dfs1(int x,int f){
fa[x]=f;
siz[x]=1;
dep[x]=dep[f]+1;
for(int i=head[x];~i;i=edge[i].next){
int v=edge[i].to;
int w=edge[i].w;
if(v==f) continue;
dis[v]=dis[x]+w;
dfs1(v,x);
siz[x]+=siz[v];
if(siz[v]>siz[son[x]]) son[x]=v;
}
}
int top[maxn];
void dfs2(int x,int t){
top[x]=t;
if(!son[x]) return ;
dfs2(son[x],t);
for(int i=head[x];~i;i=edge[i].next){
int v=edge[i].to;
if(v==fa[x]||v==son[x]) continue;
dfs2(v,v);
}
}
int LCA(int x,int y){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
x=fa[top[x]];
}
return dep[x]<dep[y] ?x:y;
}
struct Case{
int x,y,len;
}a[maxn][23];
int mn[maxn];
inline int get_dis(int x,int y){
int lca=LCA(x,y);
return dis[x]+dis[y]-2*dis[lca];
}
Case stmer(Case x,Case y){
Case res={0,0,max(x.len,y.len)};
if(x.len>y.len) res=x;
else res=y;
int a=x.x,b=x.y,c=y.x,d=y.y;
int now=get_dis(a,c);
if(now>res.len) res={a,c,now};
now=get_dis(a,d);
if(now>res.len) res={a,d,now};
now=get_dis(b,c);
if(now>res.len) res={b,c,now};
now=get_dis(b,d);
if(now>res.len) res={b,d,now};
return res;
}
void init(int n){
mn[0]=-1;
for(int i=1;i<=n;i++){
mn[i]=mn[i>>1]+1;
}
for(int j=1;j<=mn[n];j++){
for(int i=1;i+(1<<j)-1<=n;i++){
a[i][j]=stmer(a[i][j-1],a[i+(1<<(j-1))][j-1]);
}
}
}
Case query(int l,int r){
int k=mn[r-l+1];
return stmer(a[l][k],a[r-(1<<k)+1][k]);
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
memset(head,-1,sizeof head);
int n;
cin>>n;
for(int i=1;i<n;i++){
int x,y,z;
cin>>x>>y>>z;
add(x,y,z);
add(y,x,z);
a[i][0].x=a[i][0].y=i;
}
a[n][0].x=a[n][0].y=n;
dfs1(1,0);
dfs2(1,1);
init(n);
int q;
cin>>q;
while(q--){
int x,y,z,s;
cin>>x>>y>>z>>s;
Case xx=query(x,y);
Case yy=query(z,s);
Case res={0,0,0};
int a=xx.x,b=xx.y,c=yy.x,d=yy.y;
int now=get_dis(a,c);
if(now>res.len) res={a,c,now};
now=get_dis(a,d);
if(now>res.len) res={a,d,now};
now=get_dis(b,c);
if(now>res.len) res={b,c,now};
now=get_dis(b,d);
if(now>res.len) res={b,d,now};
cout<<res.len<<endl;
}
}
以上是关于[51nod] 1766树上的最远点对 树的直径 树剖LCA+ST表静态查询的主要内容,如果未能解决你的问题,请参考以下文章