UESTC - 92 LCA 倍增

本文介绍了一个基于LCA(最近公共祖先)算法的问题解决案例。在原有的图基础上增加一条边后,探讨了这条新边是否能缩短两个城市间的旅行时间,并详细讲解了如何通过LCA算法确定最短路径。

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

Bob has traveled to byteland, he find the NN cities in byteland formed a tree structure, a tree structure 
is very special structure, there is exactly one path connecting each pair of nodes, and a tree with NN 
nodes has N1N−1 edges.

As a traveler, Bob wants to journey between those NN cities, and he know the time each road will cost. 
he advises the king of byteland building a new road to save time, and then, a new road was built. Now Bob 
has QQ journey plan, give you the start city and destination city, please tell Bob how many time is saved 
by add a road if he always choose the shortest path. Note that if it's better not journey from the new roads, 
the answer is 00.

Input

First line of the input is a single integer TT(1T201≤T≤20), indicating there are TT test cases.

For each test case, the first will line contain two integers NN(2N1052≤N≤105) and QQ(1Q1051≤Q≤105), indicating 
the number of cities in byteland and the journey plans. Then NN line followed, each line will contain three 
integer xxyy(1x,yN1≤x,y≤N) and zz(1z10001≤z≤1000) indicating there is a road cost zz time connect 
the xthxth city and the ythyth city, the first N1N−1 roads will form a tree structure, indicating the original roads, 
and the NthNth line is the road built after Bob advised the king. Then QQline followed, each line will contain two 
integer xx and yy(1x,yN1≤x,y≤N), indicating there is a journey plan from the xthxth city to ythyth city.

Output

For each case, you should first output Case #t: in a single line, where tt indicating the case number between 11 and TT
then QQ lines followed, the ithith line contains one integer indicating the time could saved in ithith journey plan.

Sample Input
1
5 5
1 2 3
2 3 4
4 1 5
3 5 1
3 1 5
1 2
1 3
2 5
3 4
4 5
Sample Output
Case #1:
0
2
0
2

2

题意:在原有图的基础上加一条边。问加边之后是不是减少了距离,如果减少了就输出减少了多少。

如果没减少就输出0.

LCA水题

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
//#pragma comment(linker, "/STACK:102400000,102400000") //不需要申请系统栈
const int N = 1e5+100;
const int M = 35;
int dp[2*N][M];  //这个数组记得开到2*N,因为遍历后序列长度为2*n-1
bool vis[N];
struct edge
{
    int u,v,w,next;
}e[2*N];
int tot,head[N];
inline void add(int u ,int v ,int w ,int &k)
{
    e[k].u = u; e[k].v = v; e[k].w = w;
    e[k].next = head[u]; head[u] = k++;
    u = u^v; v = u^v; u = u^v;
    e[k].u = u; e[k].v = v; e[k].w = w;
    e[k].next = head[u]; head[u] = k++;
}
int ver[2*N],R[2*N],first[N],dir[N];
//ver:节点编号 R:深度 first:点编号位置 dir:距离
void dfs(int u ,int dep)
{
    vis[u] = true; ver[++tot] = u; first[u] = tot; R[tot] = dep;
    for(int k=head[u]; k!=-1; k=e[k].next)
        if( !vis[e[k].v] )
        {
            int v = e[k].v , w = e[k].w;
            dir[v] = dir[u] + w;
            dfs(v,dep+1);
            ver[++tot] = u; R[tot] = dep;
        }
}
void ST(int n)
{
    for(int i=1;i<=n;i++)
        dp[i][0] = i;
    for(int j=1;(1<<j)<=n;j++)
    {
        for(int i=1;i+(1<<j)-1<=n;i++)
        {
            int a = dp[i][j-1] , b = dp[i+(1<<(j-1))][j-1];
            dp[i][j] = R[a]<R[b]?a:b;
        }
    }
}
//中间部分是交叉的。
int RMQ(int l,int r)
{
    int k=0;
    while((1<<(k+1))<=r-l+1)
        k++;
    int a = dp[l][k], b = dp[r-(1<<k)+1][k]; //保存的是编号
    return R[a]<R[b]?a:b;
}

int LCA(int u ,int v)
{
    int x = first[u] , y = first[v];
    if(x > y) swap(x,y);
    int res = RMQ(x,y);
    int cnt=ver[res];
    return dir[u]+dir[v]-2*dir[cnt];
}



int main()
{
    //freopen("Input.txt","r",stdin);
    //freopen("Out.txt","w",stdout);
    int t;
    scanf("%d",&t);
    for(int cas=1;cas<=t;cas++)
    {
        int n,q,num = 0;
        scanf("%d%d",&n,&q);
        memset(head,-1,sizeof(head));
        memset(vis,false,sizeof(vis));
        for(int i=1; i<n; i++)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            add(u,v,w,num);
        }
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        printf("Case #%d:\n",cas);
        tot = 0; dir[1] = 0;
        dfs(1,1);
        ST(2*n-1);
        while(q--)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            int lca = LCA(u,v);//u v 之间的距离
           // cout<<lca<<"----------"<<endl;
            int lca1=min(LCA(u,a)+LCA(v,b),LCA(u,b)+LCA(v,a))+c;
            lca-=lca1;
            //cout<<lca1<<"*********"<<endl;
            if(lca<0) lca=0;
            printf("%d\n",lca);
        }
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值