csu 1798(树上最远点对,线段树+lca)

1798: 小Z的城市

Time Limit: 5 Sec  Memory Limit: 128 MB
Submit: 60  Solved: 16
[ Submit][ Status][ Web Board]

Description

小Z身为A国的君王,A国的所有城市都归小A所有,A国中从任意一个城市出发都能到达其他任意一个城市,每两个直接相邻的城市xi , yi之间都有一条已知长度的路径相连。A国共有n个城市(编号为1~n),被n-1条无向边相连。小Y和小X的开国功臣,但是小X擅文,小Y好武,所以他们关系很差,现在小Z想在区间[l ,r]的城市中选择两个奖赏这两位开国功臣一人一个城市,彼此厌恶的小X和小Y想让聪明的你飞到A国帮助他们做出选择可以距离彼此更远。

(也就是说最后小X会选择城市idx(l<=idx<=r) , 小Y会选择城市idy(l<=idy<=r) , 保证idx!=idy 而且idx距离idy尽可能远)。

Input

多组数据

每组数据第一行两个正整数n和q 表示A国有n个城市,q次询问 ( 2<=n <=100000 , q<=100000 )

接下来n-1行,每行输入3个正整数xi , yi , di 表示城市xi与yi被一条长度为di的路径相连

(1<=xi , yi<=n , di<=100)

接下来q个询问,每次询问输入一行li和ri,对于每次询问希望在li和ri区间选择两个城市分给小X和小Y,使他们距离最大 (1<=li < ri<=n)

所有数据保证Σn <=500000

Output

每次询问输出一个使小X和小Y距离的最大值

Sample Input

5 1
1 2 1
2 3 2
1 4 3
4 5 4
2 5

Sample Output

10



题解:线段树+lca .. 首先要知道一个这样的东西,假如一棵子树 A和另外一棵子树 B合并成 C,那么C 里面的最远点对是原来的A树里面最远点对和B树里面的最远点对这四个点中间的 2 个.所以知道了这个特性

所有的点下面的子树的最远点对就可以利用线段树进行维护了,树上最远的两个点之间的距离是 dis[u]+dis[v]-2*dis[lca(u,v)]
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef vector<int> vi;
const int N = 200005;
const int DEG = 25;
int n,q;
struct Edge
{
    int v,w,next;
} edge[N];
int head[N],tot,cost[N];
void addEdge(int u,int v,int w)
{
    edge[tot].v = v,edge[tot].w = w,edge[tot].next = head[u],head[u] = tot++;
}
void init()
{
    memset(head,-1,sizeof(head));
    tot = 0;
}
/************* lca  调用 dfs(1,0,0) 以及 getanc() ***************/
int anc[N][DEG],dep[N];
void dfs(int u,int fa,int d,int w)
{
    dep[u]=d;
    anc[u][0]=fa;
    cost[u] = w;
    for (int i=head[u]; i!=-1; i=edge[i].next)
        if (edge[i].v!=fa) dfs(edge[i].v,u,d+1,w+edge[i].w);
}

void getanc()
{
    for (int i=1; i<DEG; i++)
        for (int j=1; j<=n; j++)
            anc[j][i]=anc[anc[j][i-1]][i-1];
}

int swim(int u,int H)
{
    int i=0;
    while (H)
    {
        if (H&1) u=anc[u][i];
        i++;
        H>>=1;
    }
    return u;
}

int lca(int u,int v)
{
    if (dep[u]<dep[v]) swap(u,v);
    u=swim(u,dep[u]-dep[v]);
    if (u==v) return u;
    for (int i=DEG-1; i>=0; i--)
    {
        if (anc[u][i]!=anc[v][i])
        {
            u=anc[u][i];
            v=anc[v][i];
        }
    }
    return anc[u][0];
}
/************ segment tree *****************/
int getdistance(int a,int b)
{
    int _lca = lca(a,b);
    return cost[a]+cost[b]-2*cost[_lca];
}
vi tree[N<<2];  /// tree[i] 管辖的是以 i 为根节点的子树中相聚最远的两点
vi max_dis_point(vi a,vi b)
{
    for(int i=0; i<b.size(); i++)
    {
        a.push_back(b[i]);
    }
    vi ans;
    int _max = -1;
    for(int i=0; i<a.size(); i++)
    {
        for(int j=i+1; j<a.size(); j++)
        {
            int _distance = getdistance(a[i],a[j]);
            if(_distance>_max)
            {
                _max = _distance;
                ans.clear();
                ans.push_back(a[i]);
                ans.push_back(a[j]);
            }
        }
    }
    return ans;
}
void build(int l,int r,int idx)
{
    if(l==r)
    {
        tree[idx].push_back(l);
        tree[idx].push_back(l);
        return;
    }
    int mid = (l+r)>>1;
    build(l,mid,idx<<1);
    build(mid+1,r,idx<<1|1);
    tree[idx] = max_dis_point(tree[idx<<1],tree[idx<<1|1]);
}
vi query(int l,int r,int L,int R,int idx)
{
    vi ans;
    ans.clear();
    if (r < L || R < l) return ans;
    if(l >= L&& r <= R)
    {
        return tree[idx];
    }
    else
    {
        vi vl,vr;
        int mid = (l+r)>>1;
        vl =  query(l,mid,L,R,idx<<1);
        vr =  query(mid+1,r,L,R,idx<<1|1);
        return max_dis_point(vl,vr);
    }

}
int main()
{
    while(scanf("%d%d",&n,&q)!=EOF)
    {
        init();
        for(int i=1; i<=4*n; i++) tree[i].clear();
        for(int i=1; i<n; i++)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            addEdge(u,v,w);
            addEdge(v,u,w);
        }
        dfs(1,0,0,0);
        getanc();
        build(1,n,1);
        while(q--)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            vi ans = query(1,n,a,b,1);
            int res = 0;
            if(ans.size()==1)
            {
                res = getdistance(ans[0],ans[0]);
            }
            else res = getdistance(ans[0],ans[1]);
            printf("%d\n",res);
        }
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/liyinggang/p/5943907.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值