German Collegiate Programming Contest 2018 M-Mountaineers (建树,最小生成树,LCA)

题目:http://codeforces.com/group/xrTA2IaQje/contest/256790/attachments/download/8067/2018-german-collegiate-programming-contest-gcpc-18-en.pdf

题意:每个点一个权值,q个询问,每次询问从一点到另一点的路径经过的最小的最大值。

思路:建图,每个点和相邻的点都连上边,如果构成了一个环,那么删掉环中权值最大的边一定对答案无影响,所以最后建成的是一棵树,用最小生成树的方式建树即可。询问时求两点LCA过程中得出最大值。

#include <bits/stdc++.h>
#define LL long long
using namespace std;
typedef pair<int, int> P;
const int maxn = 3e5+5;

struct Edge{
    int u, v, w;
    bool operator < (const Edge & e) const {
        return w < e.w;
    }
}edge[maxn<<1];

int n, m, q, tot;
int fa[maxn], v[maxn], val[maxn];
std::vector<P> e[maxn];
int Find(int x){return x == fa[x] ? x : fa[x] = Find(fa[x]);}
void Merge(int x, int y){
    int fx = Find(x), fy = Find(y);
    fa[fy] = fx;
}
int chg(int x, int y) {return x*m+y;}

void Build_Tree(){
    for(int i=1; i<=tot; i++){
        int u = edge[i].u, v = edge[i].v, w = edge[i].w;
        if(Find(u) == Find(v)) continue;
        Merge(u, v);
        e[u].push_back(P(v, w)); e[v].push_back(P(u, w));
    }
}

int d[maxn], f[maxn][21], Max[maxn][21];
void dfs2(int u, int fa){
    d[u] = d[fa] + 1; f[u][0] = fa;
    Max[u][0] = max(v[u], v[fa]);
    for(int i=1; i<=20; i++){
        f[u][i] = f[f[u][i-1]][i-1];
        Max[u][i] = max(Max[u][i], max(Max[u][i-1], Max[f[u][i-1]][i-1]));
    }
    for(auto it : e[u]) if(it.first != fa) dfs2(it.first, u);
}

int lca(int u, int x){
    int mx = max(v[u],v[x]);
    if(d[u] > d[x]) swap(u, x);
    for(int i=20; i>=0; i--)
        if(d[u] <= d[x]-(1<<i))
            mx = max(mx, Max[x][i]), x = f[x][i];
    if(u == x) return mx; // u
    for(int i=20; i>=0; i--){
        if(f[u][i] != f[x][i])
            mx = max(mx, max(Max[u][i], Max[x][i])), u = f[u][i], x = f[x][i];
    }
    // cout << u << " " << x << " " << mx << endl;
    return max(mx, v[f[x][0]]); //f[u][0]
}

int main(){
    for(int i=1; i<maxn; i++) fa[i] = i;
    cin >> n >> m >> q;
    for(int i=1; i<=n; ++i){
        for(int j=1; j<=m; ++j)
            cin >> v[chg(i, j)];

        for(int j=1; j<=m; j++){
            if(j<m) edge[++tot] = Edge{chg(i, j), chg(i, j+1), max(v[chg(i, j)], v[chg(i, j+1)])};
            if(i > 1) edge[++tot] = Edge{chg(i-1, j), chg(i, j), max(v[chg(i-1,j)], v[chg(i, j)])};
        }
    }

    sort(edge+1, edge+1+tot);

    Build_Tree();

    /*for(int i=1; i<=100; i++){
        for(auto it : e[i]){
            printf("%d*(%d,%d)--%d\n", v[i], i, it.first, it.second);
        }
    }*/

    dfs2(chg(1, 1), chg(1, 1));

   /* for(int i=1; i<=100; i++){
        if(Max[i][0] == 0) continue;
        cout << i << " " << Max[i][0] << " " << Max[i][1] << " " << Max[i][2] << endl;
    }*/
    int x1, y1, x2, y2;
    while(q--){
        cin >> x1 >> y1 >> x2 >> y2;
        cout << lca(chg(x1, y1), chg(x2, y2)) << endl;
    }
}
/*
2 3 3
1 3 2 
2 4 5
*/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值