[51nod 1766]树上的最远点对

题目大意

给定一棵树,m个询问每次询问从a~b选一个结点并从c~d选一个结点使这两个结点距离最大。

结论

根据树的直径性质我们得到一个结论:
一个集合中的直径两端点一定被分成两个非空集合后两条直径的四个端点包含。
于是线段树维护。
注意你求lca不能倍增,应该用RMQ求。
RMQ求LCA的方法:得到树的欧拉序与对应深度序,设fi[x]表示结点x最早出现在欧拉序中的位置。假若满足fi[x]<=z<=fi[y]的一个z是深度序中[fi[x],fi[y]]最小的,那么dfn[z]就是x与y的LCA。

#include<cstdio>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=100000+10;
int h[maxn],go[maxn*2],dis[maxn*2],next[maxn*2],d[maxn],dep[maxn],f[maxn*2][25],dfn[maxn*2],fi[maxn];
struct dong{
    int x,y;
} tree[maxn*5];
int i,j,k,l,t,n,m,tot,ans,top;
dong ans1,ans2;
int lca(int x,int y){
    x=fi[x];y=fi[y];
    if (x>y) swap(x,y);
    int z=floor(log(y-x+1)/log(2));
    if (dep[dfn[f[x][z]]]<dep[dfn[f[y-(1<<z)+1][z]]]) return dfn[f[x][z]];else return dfn[f[y-(1<<z)+1][z]];
}
int getdis(int x,int y){
    return d[x]+d[y]-d[lca(x,y)]*2;
}
void merge(dong a,dong b,dong &c){
    if (a.x==0){
        c=b;
        return;
    }
    else if (b.x==0){
        c=a;
        return;
    }
    int i,j,x=0,y=0,l=0,t;
    int e[5];
    e[1]=a.x;e[2]=a.y;e[3]=b.x;e[4]=b.y;
    fo(i,1,3)
        fo(j,i+1,4){
            t=getdis(e[i],e[j]);
            if (t>l){
                l=t;
                x=e[i];
                y=e[j];
            }
        }
    c.x=x;c.y=y;
}
void add(int x,int y,int z){
    go[++tot]=y;
    dis[tot]=z;
    next[tot]=h[x];
    h[x]=tot;
}
void dfs(int x,int y){
    dep[x]=dep[y]+1;
    dfn[++top]=x;
    fi[x]=top;
    int t=h[x];
    while (t){
        if (go[t]!=y){
            d[go[t]]=d[x]+dis[t];
            dfs(go[t],x);
            dfn[++top]=x;
        }
        t=next[t];
    }
}
void build(int p,int l,int r){
    if (l==r){
        tree[p].x=tree[p].y=l;
        return;
    }
    int mid=(l+r)/2;
    build(p*2,l,mid);
    build(p*2+1,mid+1,r);
    merge(tree[p*2],tree[p*2+1],tree[p]);
}
void query(int p,int l,int r,int a,int b){
    if (l==a&&r==b){
        merge(ans1,tree[p],ans1);
        return;
    }
    int mid=(l+r)/2;
    if (b<=mid) query(p*2,l,mid,a,b);
    else if (a>mid) query(p*2+1,mid+1,r,a,b);
    else{
        query(p*2,l,mid,a,mid);
        query(p*2+1,mid+1,r,mid+1,b);
    }
}
int read(){
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
int main(){
    //freopen("tree.in","r",stdin);
    n=read();
    fo(i,1,n-1){
        j=read();k=read();l=read();
        add(j,k,l);
        add(k,j,l);
    }
    dfs(1,0);
    fo(i,1,top) f[i][0]=i;
    fo(j,1,log(top)/log(2))
        fo(i,1,top-(1<<j)+1)
            if (dep[dfn[f[i][j-1]]]<dep[dfn[f[i+(1<<(j-1))][j-1]]]) f[i][j]=f[i][j-1];else f[i][j]=f[i+(1<<(j-1))][j-1];
    build(1,1,n);
    m=read();
    while (m--){
        j=read();k=read();l=read();t=read();
        ans1.x=0;
        query(1,1,n,j,k);
        ans2=ans1;
        ans1.x=0;
        query(1,1,n,l,t);
        ans=getdis(ans1.x,ans2.x);
        t=getdis(ans1.x,ans2.y);
        if (t>ans) ans=t;
        t=getdis(ans1.y,ans2.y);
        if (t>ans) ans=t;
        t=getdis(ans1.y,ans2.x);
        if (t>ans) ans=t;
        printf("%d\n",ans);
    }
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值