UVA11354 Bond

很明显满足这个条件的路径就是最小生成树上链接两个点的那条唯一的路径


但是题目有50000个询问,如果单纯的在MST上DFS求值则每次查询的复杂度都是O(n),最后可能高达50000*50000

如果组成一棵有根树,每个点记录了父节点及与父节点距离,则任意两个点肯定会有一个共同祖先,所求的值就是两个点到共同祖先路径上最大边中的较大者,

我们只要记录每个点的深度,先将深度调到一样大,然后一起向上搜索肯定会搜到那个共同祖先(也一边更新两个最大边),每个查询复杂度就是这条路径的长度了,构优化了

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
const int maxn = 50000+10;
const int maxm = 100000+10;
struct Edge
{
    int from, to, len;
    Edge(int from, int to, int len)
    {
        this->from = from;
        this->to = to;
        this->len = len;
    }
    bool operator<(const struct Edge &ans)const
    {
        return len > ans.len;
    }
};
vector<pair<int, int> > adj[maxn];
int pre[maxn], dis[maxn], depth[maxn];//MST的父节点和与父节点的距离、深度
bool vis[maxn];
int dist[maxn];//到MST集合的距离
int n, m, q;
void Init()
{
    int a, b, c;
    for(int i = 1; i <= n; i++)
        adj[i].clear();
    for(int i = 1; i <= m; i++)
    {
        scanf("%d%d%d", &a, &b, &c);
        adj[a].push_back(make_pair(b, c));
        adj[b].push_back(make_pair(a, c));
    }
}

void Prim()
{
    priority_queue<Edge> myQue;
    for(int i = 1; i <= n; i++)
        vis[i] = false, dist[i] = -1;

    pre[1] = -1;
    depth[1] = 0;
    vis[1] = true;//MST根节点

    for(vector<pair<int, int> >::iterator it = adj[1].begin(); it != adj[1].end(); it++)
    {
        dist[it->first] = it->second;
        myQue.push(Edge(1, it->first, it->second));
    }

    while(!myQue.empty())
    {
        Edge ans = myQue.top();
        myQue.pop();
        int u = ans.to;
        if(vis[u])
            continue;
        pre[u] = ans.from;
        depth[u] = depth[ans.from]+1;
        vis[u] = true;
        dis[u] = ans.len;//到父节点的距离
        for(vector<pair<int, int> >::iterator it = adj[u].begin(); it != adj[u].end(); it++)
        {
            int v = it->first;
            if(!vis[v] && (dist[v] == -1 || dist[v] > it->second))
            {
                dist[v] = it->second;
                myQue.push(Edge(u, v, dist[v]));
            }
        }
    }
}

int Query(int a, int b)//a的深度<=b的深度
{
    int m1 = -1, m2 = -1;
    while(depth[a] != depth[b])//将深度调到一样
    {
        m2 = max(m2, dis[b]);
        b = pre[b];
    }
    while(a != b)
    {
        m1 = max(m1, dis[a]);
        m2 = max(m2, dis[b]);
        a = pre[a], b = pre[b];
    }
    return max(m1, m2);
}

void Solve()
{
    int a, b;
    scanf("%d", &q);
    for(int i = 1; i <= q; i++)
    {
        scanf("%d%d", &a, &b);
        if(depth[a] > depth[b])
            swap(a, b);
        printf("%d\n", Query(a, b));
    }
}

int main()
{
    int tcase = 0;
    while(scanf("%d%d", &n, &m) != EOF)
    {
        Init();
        if(tcase++ != 0)
            puts("");
        Prim();
        Solve();
    }
    return 0;
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值