【51NOD 1766】树上的最远点对

题目

n个点被n-1条边连接成了一颗树,给出ab和cd两个区间,表示点的标号请你求出两个区间内各选一点之间的最大距离,即你需要求出max{dis(i,j) |a<=i<=b,c<=j<=d}
(PS 建议使用读入优化)

思路

对于每一个结点维护其到子树内叶子的最长链和次长链(不能相交),这样dp的话可以方便求出子树内的直径,子树内的直径就是所有节点的最长链长度+次长链长度的最大值。

代码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<utility>
#include<vector>
#include<iostream>
using namespace std;
 
#define debug(a) cout<<#a<<"="<<a<<" "
#define mpr make_pair
typedef pair< int,int > pr;
typedef long long LL;
const int N = 100050;
const int M = 25;
 
int n,m,cnt;
vector< pr > g[N];
int pow2[M],lg2[N<<1],dfs[N<<1],d[N],val[N],pos[N];
int f[N<<1][M];
 
inline int in(int x=0,char ch=getchar()){ while(ch>'9' || ch<'0') ch=getchar();
    while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x; }
void DFS(int u,int fa,int dep,int value){
    dfs[++m]=u,d[u]=dep,val[u]=value,pos[u]=m,f[m][0]=u;
    for(int i=0,v,lim=g[u].size();i<lim;i++) if((v=g[u][i].first)!=fa) DFS(v,u,dep+1,value+g[u][i].second),dfs[++m]=u,f[m][0]=u;
}
void init(){
    pow2[0]=1;for(int i=1;i<M;i++) pow2[i]=pow2[i-1]<<1;
    lg2[0]=-1;for(int i=1;i<=m;i++) lg2[i]=lg2[i>>1]+1;
    for(int j=1;j<M;j++) for(int i=1;i<=m;i++) if(i+pow2[j]-1<=m){
        int u=f[i][j-1],v=f[i+pow2[j-1]][j-1];
        if(d[u]<d[v]) f[i][j]=u;else f[i][j]=v;
    }
}
int Dis(int u,int v,int lca=0){
    if(pos[u]<pos[v]) swap(u,v);int lg=lg2[pos[u]-pos[v]+1];
    if(d[f[pos[v]][lg]]<d[f[pos[u]-pow2[lg]+1][lg]]) lca=f[pos[v]][lg];else lca=f[pos[u]-pow2[lg]+1][lg];
    return (LL)val[u]+val[v]-2*val[lca];
}
struct SegmentTree{
    #define lc (o<<1)
    #define rc (o<<1|1)
    #define mid ((l+r)>>1)
    #define Gd(u) Dis(u.first,u.second)
    pr g[N<<2];int d[N<<2];
    pr PushUp(pr u,pr v,int d1=0,int d2=0,int o=0){
        if(!d1 && !d2) d1=Gd(u),d2=Gd(v);
        pr res=d1>d2?u:v;int dd=max(d1,d2);
        if(Dis(u.first,v.first)>dd) res=mpr(u.first,v.first),dd=Gd(res);
        if(Dis(u.first,v.second)>dd) res=mpr(u.first,v.second),dd=Gd(res);
        if(Dis(u.second,v.first)>dd) res=mpr(u.second,v.first),dd=Gd(res);
        if(Dis(u.second,v.second)>dd) res=mpr(u.second,v.second),dd=Gd(res);
        if(o) d[o]=dd;return res;
    }
    void Build(int o,int l,int r){
        if(l==r){ g[o]=mpr(l,l),d[o]=0;return; }
        Build(lc,l,mid);Build(rc,mid+1,r);
        g[o]=PushUp(g[lc],g[rc],d[lc],d[rc],o);
    }
    pr Query(int o,int l,int r,int L,int R){
        if(L<=l && r<=R) return g[o];
        pr res=mpr(L,L);
        if(L<=mid) res=Query(lc,l,mid,L,R);
        if(R>mid) res=PushUp(res,Query(rc,mid+1,r,L,R));
        return res;
    }
    pr Merge(pr u,pr v){
        pr res=mpr(u.first,v.first);int d=Gd(res);
        if(Dis(u.first,v.second)>d) res=mpr(u.first,v.second),d=Gd(res);
        if(Dis(u.second,v.first)>d) res=mpr(u.second,v.first),d=Gd(res);
        if(Dis(u.second,v.second)>d) res=mpr(u.second,v.second),d=Gd(res);
        return res;
    }
    int Query(int a,int b,int c,int d){
        pr r1=Query(1,1,n,a,b),r2=Query(1,1,n,c,d),r3=Merge(r1,r2);
        return Gd(r3);
    }
    #undef lc
    #undef rc
    #undef mid
    #undef Gd
}seg;
int main(){
    n=in();memset(d,0x3f,sizeof(d));
    for(int i=1,u,v,w;i<n;i++) u=in(),v=in(),w=in(),g[u].push_back(mpr(v,w)),g[v].push_back(mpr(u,w));
    DFS(1,1,1,0),init(),seg.Build(1,1,n);
    for(int k=in(),a,b,c,d;k--;){
        a=in(),b=in(),c=in(),d=in();
        printf("%d\n",seg.Query(a,b,c,d));
    }return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值