【51Nod1766】树上的最远点对 线段树+LCA

原题走这里

正解:对于每个询问以a~b,c~d之间的点为关键点建虚树,然后枚举a到b中的每一个点,bfs求最长路即可

原题中存在区间询问,因此八成会扯到数据结构,尤其是线段树。
然而……线段树维护区间最长路?

首先我们有这样一个结论:
对于两棵树,设它们的直径/最长路分别为(a,b)(c,d)
则两棵树合并成新树后,最长路的端点也必然在a,b,c,d四个点之中。计算的时候枚举四个点中的任意两个,求树上距离就可以了。
利用这样一个结论,我们就可以合并区间的答案,于是我们就可以用线段树来维护区间的最长路了。

此外还有一个小问题,鉴于本方法要算如此多次LCA,明显应该使用ST表这类O(1)的算法,否则怕是得TLE上天。
事实上上面那个结论非常好用,可以很容易求出任意点集的最远点对等等,由此衍生出了一系列极其玄妙的题,如JZOJ4587(Snow的追寻)以及并查集+树的直径之类的东西。

具体实现见代码如下:

#include <bits/stdc++.h>
using namespace std;
int read()
{
    int X=0;char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9')X=X*10+ch-'0',ch=getchar();
    return X;   
}
struct edge
{
    int v,l,p;
}e[200100];
struct node
{
    int u,v;
}t[400000];
int n,m,head[100010],top,dfs_clock,st[20][300000],d[100010],dfn[300010],lg2[300010],pl[100010];
void addedge(int u,int v,int l)
{
    e[++top]=(edge){v,l,head[u]};
    head[u]=top;
    e[++top]=(edge){u,l,head[v]};
    head[v]=top;
}
void dfs(int u,int fa)
{
    dfn[++dfs_clock]=u;
    pl[u]=dfs_clock;
    for(int i=head[u];i;i=e[i].p)
    {
        int v=e[i].v;
        if(v==fa)continue;
        d[v]=d[u]+e[i].l;
        dfs(v,u);
        dfn[++dfs_clock]=u;
    }
}
void build_st()
{
    for(int i=1,j=-1;i<=dfs_clock;i++)
    {
        if(i==(i&-i))j++;
        lg2[i]=j;
        st[0][i]=d[dfn[i]];
//      cout<<d[dfn[i]]<<' ';
    }
//  cout<<endl;
    for(int i=1;i<=lg2[dfs_clock];i++)
    {
        for(int j=1;j<=dfs_clock-(1<<(i-1));j++)
        {
            st[i][j]=min(st[i-1][j],st[i-1][j+(1<<(i-1))]);
        }
    }
}
inline int dist(int u,int v)
{
    if(u==v)return 0;
    if(pl[u]>pl[v])swap(u,v);
    int temp=lg2[pl[v]-pl[u]+1];
//  cout<<"----------------"<<u<<' '<<v<<' '<<d[u]<<' '<<d[v]<<' '<<temp<<' '<<pl[u]<<' '<<pl[v]<<' '<<st[temp][pl[u]]<<' '<<st[temp][pl[v]-(1<<temp)+1]<<' '<<min(st[temp][pl[u]],st[temp][pl[v]-(1<<temp)+1])<<endl;
    return d[u]+d[v]-2*min(st[temp][pl[u]],st[temp][pl[v]-(1<<temp)+1]);
}
void merge(int u1,int v1,int u2,int v2,int &ans1,int &ans2,bool b=0)
{
    if((u1==0&&v1==0)||(u2==0&&v2==0))
    {
        ans1=u1+u2;
        ans2=v1+v2;
        return;
    }
    int ret=0,dd[6]={dist(u1,u2),dist(u1,v2),dist(v1,u2),dist(v1,v2),dist(u1,v1),dist(u2,v2)};
    int ee[6][2]={{u1,u2},{u1,v2},{v1,u2},{v1,v2},{u1,v1},{u2,v2}};
    for(int i=0;i<(b?4:6);i++)
    {
        ret=max(dd[i],ret);
        if(ret==dd[i])ans1=ee[i][0],ans2=ee[i][1];
    }
}
void build(int k,int l,int r)
{
    if(l==r)
    {
        t[k]=(node){l,r};
        return;
    }
    int mid=(l+r)>>1;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
    merge(t[k<<1].u,t[k<<1].v,t[k<<1|1].u,t[k<<1|1].v,t[k].u,t[k].v);
//  cout<<"-------------"<<k<<' '<<l<<' '<<r<<' '<<t[k].u<<' '<<t[k].v<<endl;
}
void ask(int k,int l,int r,int L,int R,int &ans1,int &ans2)
{
    if(L<=l&&r<=R)
    {
        ans1=t[k].u;
        ans2=t[k].v;
        return;
    }
    int mid=(l+r)>>1,u1=0,v1=0,u2=0,v2=0;
    if(L<=mid)ask(k<<1,l,mid,L,R,u1,v1);
    if(R>mid)ask(k<<1|1,mid+1,r,L,R,u2,v2);
    merge(u1,v1,u2,v2,ans1,ans2);
//  cout<<k<<' '<<l<<' '<<r<<' '<<L<<' '<<R<<' '<<ans1<<' '<<ans2<<endl;
}
int query(int l1,int r1,int l2,int r2)
{
    int u1,v1,u2,v2,w1,w2;
    ask(1,1,n,l1,r1,u1,v1);
    ask(1,1,n,l2,r2,u2,v2);
    merge(u1,v1,u2,v2,w1,w2,1);
    return dist(w1,w2);
}
int main()
{
    cin>>n;
    for(int i=1;i<n;i++)
    {
        int u=read(),v=read(),l=read();
        addedge(u,v,l);
    }
    dfs(1,0);
    build_st();
    build(1,1,n);
    cin>>m;
    for(int i=1;i<=m;i++)
    {
        int A=read(),B=read(),C=read(),D=read();
        cout<<query(A,B,C,D)<<endl;
    }
    return 0;
}
阅读更多

没有更多推荐了,返回首页