51Nod1766: 树上的最远点对 题解

本文探讨了树的直径性质,特别是如何选取直径端点,并利用这一性质来解决51Nod的一道题目。通过线段树实现,区间合并遵循树的直径性质,同时为了满足O(nlogn)的时间复杂度,使用ST表进行O(1)的最近公共祖先(LCA)查询。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这题用到了树的直径的非常好的性质,有必要记录
树的直径满足这样一个性质:
设一棵树的一个点集SS的直径的端点集合是{a1,b1}(如果有多条直径选取任意一条),另一个点集TT的直径的端点集合是{a2,b2},则点集STS⋃T的直径的端点集合{a3,b3a3,b3}{a1,b1,a2,b2a1,b1,a2,b2}
证明不给出,大概是反证,如果最终的直径不在这四个点中的话,刚开始有一个点集的直径就不是最长的
为什么有多条直径可以随便取呢,因为考虑存在多条直径的情况,一定是有若干条长度为偶数的直径在中点处相交,这个形状是完全对称的
所以我们考虑线段树,线段树上[l,r]维护了l~r的点构成的树的直径,区间的合并参照上面的性质合并就好
注意这题只能允许O(nlogn)O(nlogn)的复杂度,所以要O(1)O(1)求LCA,可以用ST表算RMQ的方法做到这一点

#include <bits/stdc++.h>
using namespace std;

#define LL long long
#define LB long double
#define ull unsigned long long
#define x first
#define y second
#define pb push_back
#define pf push_front
#define mp make_pair
#define Pair pair<int,int>
#define pLL pair<LL,LL>
#define pii pair<double,double>
#define LOWBIT(x) x & (-x)

const int INF=2e9;
const LL LINF=2e16;
const int MOD=998244353;
const int magic=348;
const double eps=1e-10;
const double pi=acos(-1);

inline int getint()
{
    bool f;char ch;int res;
    while (!isdigit(ch=getchar()) && ch!='-') {}
    if (ch=='-') f=false,res=0; else f=true,res=ch-'0';
    while (isdigit(ch=getchar())) res=res*10+ch-'0';
    return f?res:-res;
}

const int MAXN=3e5;

int n,q;
vector<Pair> v[MAXN+48];

int depth[MAXN+48],seq[MAXN+48],pos[MAXN+48],fa[MAXN+48],ind=0;
int ST[MAXN+48][21],mpos[MAXN+48][21];int Log[MAXN+48];

inline void dfs(int cur,int father)
{
    fa[cur]=father;int i,y;pos[cur]=++ind;seq[ind]=cur;
    for (i=0;i<int(v[cur].size());i++)
    {
        y=v[cur][i].x;
        if (y!=father) depth[y]=depth[cur]+v[cur][i].y,dfs(y,cur);
    }
    seq[++ind]=fa[cur];
}

inline void construct_ST()
{
    int i,j;
    for (i=1;i<=ind;i++) ST[i][0]=depth[seq[i]],mpos[i][0]=seq[i];
    Log[0]=Log[1]=0;for (i=2;i<=ind;i++) Log[i]=Log[i>>1]+1;
    for (j=1;j<=20;j++)
        for (i=1;i<=ind;i++)
        {
            ST[i][j]=ST[i][j-1];mpos[i][j]=mpos[i][j-1];
            if (i+(1<<(j-1))<=ind && ST[i+(1<<(j-1))][j-1]<ST[i][j])
            {
                ST[i][j]=ST[i+(1<<(j-1))][j-1];
                mpos[i][j]=mpos[i+(1<<(j-1))][j-1];
            }
        }
}

inline int getlca(int u,int v)
{
    u=pos[u];v=pos[v];if (u>v) swap(u,v);
    int k=v-u+1;
    if (ST[u][Log[k]]<ST[v-(1<<Log[k])+1][Log[k]]) return mpos[u][Log[k]]; else return mpos[v-(1<<Log[k])+1][Log[k]];
}

struct node
{
    int left,right;
    int maxdist,u,v;
    inline bool operator < (const node &other) const {return maxdist<other.maxdist;}
}tree[MAXN*4];

namespace SegmentTree
{
    inline void Update(node &cur,int curdist,int x,int y)
    {
        if (cur.maxdist<curdist)
        {
            cur.maxdist=curdist;
            cur.u=x;cur.v=y;
        }
    }
    inline void pushup(node &cur,node &x,node &y,bool type)
    {
        if (type==true)
        {
            Update(cur,x.maxdist,x.u,x.v);
            Update(cur,y.maxdist,y.u,y.v);
        }
        if (x.maxdist>=0 && y.maxdist>=0)
        {
            Update(cur,depth[x.u]+depth[y.u]-2*depth[getlca(x.u,y.u)],x.u,y.u);
            Update(cur,depth[x.u]+depth[y.v]-2*depth[getlca(x.u,y.v)],x.u,y.v);
            Update(cur,depth[x.v]+depth[y.u]-2*depth[getlca(x.v,y.u)],x.v,y.u);
            Update(cur,depth[x.v]+depth[y.v]-2*depth[getlca(x.v,y.v)],x.v,y.v);
        }
    }
    inline void build(int cur,int left,int right)
    {
        tree[cur].left=left;tree[cur].right=right;tree[cur].maxdist=-1;
        if (left!=right)
        {
            int mid=(left+right)>>1;
            build(cur<<1,left,mid);build(cur<<1|1,mid+1,right);
            pushup(tree[cur],tree[cur<<1],tree[cur<<1|1],true);
        }
        else
            tree[cur].maxdist=0,tree[cur].u=tree[cur].v=left;
    }
    inline node query(int cur,int left,int right)
    {
        if (left<=tree[cur].left && tree[cur].right<=right) return tree[cur];
        node res1;node res2;res1.maxdist=res2.maxdist=-INF;node res;res.maxdist=-1;
        int mid=(tree[cur].left+tree[cur].right)>>1;
        if (left<=mid) res1=query(cur<<1,left,right);
        if (mid+1<=right) res2=query(cur<<1|1,left,right);
        pushup(res,res1,res2,true);
        return res;
    }
}

int main ()
{
    int i,x,y,c,l1,r1,l2,r2;n=getint();
    for (i=1;i<=n-1;i++)
    {
        x=getint();y=getint();c=getint();
        v[x].pb(mp(y,c));v[y].pb(mp(x,c));
    }
    dfs(1,-1);construct_ST();
    SegmentTree::build(1,1,n);
    node res1,res2,res;
    q=getint();
    while (q--)
    {
        l1=getint();r1=getint();l2=getint();r2=getint();
        res1=SegmentTree::query(1,l1,r1);res2=SegmentTree::query(1,l2,r2);
        res.maxdist=-1;
        SegmentTree::pushup(res,res1,res2,false);
        printf("%d\n",res.maxdist);
    }
    return 0;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值