【NOIP2013/Codevs3287】货车运输-最小生成树(大)-树上倍增

https://www.luogu.org/problemnew/show/P1967

由题可知,我们走的路的边应尽可能大,所以通过$kruscal$建最大生成树的图,再树上倍增,注意可能有多棵树;

#include <bits/stdc++.h>
#define read read()
#define up(i,l,r) for(register int i = (l);i <= (r);i++)
#define down(i,l,r) for(register int i = (l);i >= (r);i--)
#define traversal_vedge(i) for(register int i = head[u]; i ;i = e[i].nxt)
#define ll long long
using namespace std;
int read
{
    int x = 0, f = 1; char ch = getchar();
    while(ch < 48 || ch > 57) {if(ch == '-')f = -1; ch = getchar();}
    while(ch >=48 && ch <=57) {x = 10 * x + ch - 48;ch = getchar();}
    return x * f; 
}
const int N = 1e4+5,M = 5e5+5,inf = 0x3f3f3f3f;
int n,m,q,f[N];
struct kedge{
    int u,v,limit;
    bool operator < (const kedge &x) const{
        return limit > x.limit;
    }
}ke[M<<1];

struct edge{
    int v,limit,nxt;
}e[M<<1];int tot,head[N];

void build_tree(int u,int v,int w) {e[++tot] = (edge){v,w,head[u]}; head[u] = tot;}

//-----------------------------------------------------------------
int find(int i) {if(f[i] == i) return i;f[i] = find(f[i]);return f[i]; }
void kruscal(){
    sort(ke+1,ke+m+1);
    up(i,1,n) f[i] = i;
    int cnt = 0;
    up(i,1,m)
    {
        if(cnt == n-1) break;
        if(find(ke[i].u) != find(ke[i].v))
        {
            f[f[ke[i].u]] = f[ke[i].v];
            cnt++;
            build_tree(ke[i].u,ke[i].v,ke[i].limit);
            build_tree(ke[i].v,ke[i].u,ke[i].limit);//debug ke[i].u -> ke[i].v;
        }
        else continue;
    }
}
//-----------------------------------------------------------------------

int fa[N][15],mine[N][15],dep[N],vis[N];//debug 15 ->14

void dfs(int u,int f,int w){
    vis[u] = 1;
    dep[u] = dep[f] + 1;
    fa[u][0] = f;
    mine[u][0] = w;
    for(int i = 1; (1<<i) <= dep[u]; i++)
    {
        fa[u][i] = fa[fa[u][i-1]][i-1];
        mine[u][i] = min(mine[u][i-1],mine[fa[u][i-1]][i-1]);
    }
    traversal_vedge(i)
    {
        int v = e[i].v;
        if(v == f) continue;
        dfs(v,u,e[i].limit);
    }
}

/*int LCA(int x,int y){
    if(dep[x] < dep[y]) swap(x,y);
    down(i,14,0)
    {
        if(dep[fa[x][i]] >= dep[y]) 
            x = fa[x][i];
    }
    if(x == y) return x;
    down(i,14,0)
    {
        if(fa[x][i] != fa[y][i])
        {
            x = fa[x][i];
            y = fa[y][i];
        }
    }
    return fa[x][0];
}*/
//-----------------------------------------------------------------

int query(int x,int y)
{
    int ans = inf;
    if(dep[x] < dep[y]) swap(x,y);
    down(i,14,0)
    {
        if(dep[fa[x][i]] >= dep[y]) 
        {
            ans = min(ans,mine[x][i]);
            x = fa[x][i];
        }
    }
    if(x == y) return ans;
    down(i,14,0)
    {
        if(fa[x][i] != fa[y][i])
        {
            ans = min(ans,min(mine[x][i],mine[y][i]));
            x = fa[x][i];
            y = fa[y][i];
        }
    }
    ans = min(ans,min(mine[x][0],mine[y][0]));
    return ans;
}

/*int query(int x,int lca){
    //if(x == lca) return 0x3f3f3f3f;
    int i = 0;
    while(dep[fa[x][i]] > dep[lca] ) i++;
    return mine[x][i];
}*/

void work(){
    kruscal();
    memset(mine,0x3f,sizeof(mine));
    //debug 未考虑多棵树
    up(i,1,n)
    {
        if(!vis[i])
        dfs(i,0,inf);
    } 
    q = read;
    while(q--)
    {
        int x = read, y = read;
        if(find(x) != find(y)) 
        {
            printf("-1\n");
            continue;
        }
        //int lca = LCA(x,y);
        //ans = min(query(x,lca),query(y,lca));
        printf("%d\n",query(x,y));
    }
}

void readdata()
{
    n = read; m = read;
    up(i,1,m)
    {
        ke[i].u = read; ke[i].v = read; ke[i].limit = read;
    }
}

int main()
{
    freopen("input21.txt","r",stdin);
    //freopen("output21.out","w",stdout);
    readdata();
    work();
    return 0;
}

最开始的写法之所以是错误的,是因为会多求一段,而我们只需要求到LCA就行;

posted @ 2019-02-22 14:45  陈星卿  阅读(181)  评论(0编辑  收藏  举报