[NOIP2013][CODEVS3287]货车运输(kruskal+树上倍增)

49 篇文章 0 订阅
19 篇文章 0 订阅

题目描述

传送门

题解

很久之前写的,发现没有发题解,于是来写一发。
用kruskal求最大生成树,然后倍增。
现在看当时的码风都非常的naive~
还有发现自己用了poj当做变量名= =吓哭了T_T

代码

#include<algorithm>  
#include<iostream>  
#include<cstring>  
#include<cstdio>  
#define MAXN 50005  
#define sz 13  
#define maxn 10005  
#define large 2100000000  
using namespace std;  
int n,m,sum,r,tot,x,y,p;  
int father[maxn],ccount[maxn],root[maxn],point[maxn],next[2*MAXN],v[2*MAXN],c[2*MAXN],h[maxn],mi[sz],f[maxn][sz],s[maxn][sz];  
struct hp{  
    int left,right,delta;  
}a[MAXN];  
int cmp(hp a,hp b){  
    return a.delta>b.delta;  
}  
int find(int x){  
    if (x==father[x]) return x; father[x]=find(father[x]); return father[x];  
}  
void merge(int x,int y){  
    int f1=find(x); int f2=find(y); father[f1]=f2;  
}  
inline void add(int x,int y,int z,int i){  
    v[i]=y; c[i]=z; next[i]=point[x]; point[x]=i;  
}  
inline void build(int x,int fa,int dep){  
    h[x]=dep;  
    for (int i=1;i<sz;++i){  
        if (h[x]-mi[i]<1) break;  
        f[x][i]=f[f[x][i-1]][i-1];  
        s[x][i]=min(s[x][i-1],s[f[x][i-1]][i-1]);  
    }  
    for (int i=point[x];i;i=next[i])  
      if (v[i]!=fa){  
        f[v[i]][0]=x;  
        s[v[i]][0]=c[i];  
        build(v[i],x,dep+1);  
      }  
}  
inline int lca(int x,int y){  
    int Min=large;  
    if (h[x]<h[y]) swap(x,y); int k=h[x]-h[y];  
    for (int i=0;i<sz;++i)  
      if ((k>>i)&1){  
        Min=min(Min,s[x][i]); x=f[x][i];  
      }  
    if (x==y) return Min;  
    for (int i=sz-1;i>=0;--i)  
      if (f[x][i]!=f[y][i]){  
        Min=min(Min,min(s[x][i],s[y][i]));  
        x=f[x][i]; y=f[y][i];  
      }  
    Min=min(Min,min(s[x][0],s[y][0]));  
    return Min;  
}  
int main(){  
    scanf("%d%d",&n,&m);  
    mi[0]=1; for (int i=1;i<sz;++i) mi[i]=mi[i-1]*2;  
    for (int i=1;i<=n;++i) father[i]=i;  
    for (int i=1;i<=m;++i){  
      scanf("%d%d%d",&a[i].left,&a[i].right,&a[i].delta);  
      if (find(a[i].left)!=find(a[i].right)) merge(a[i].left,a[i].right);     
    }  
    for (int i=1;i<=n;++i) int poj=find(i);  
    for (int i=1;i<=n;++i) ++ccount[father[i]];  
    for (int i=1;i<=n;++i)  
      if (ccount[i]){  
        sum+=ccount[i]-1; root[++r]=i;  
      }  
    sort(a+1,a+m+1,cmp);  
    for (int i=1;i<=n;++i) father[i]=i; int i=0;  
    while(tot<sum){  
        i++;  
        if (find(a[i].left)!=find(a[i].right)){  
            add(a[i].left,a[i].right,a[i].delta,(++tot)<<1);  
            add(a[i].right,a[i].left,a[i].delta,tot<<1|1);  
            merge(a[i].left,a[i].right);  
        }  
    }  
    memset(s,127/3,sizeof(s));  
    for (int i=1;i<=r;++i){  
        int tmp=root[i]; build(tmp,0,1);  
    }  
    scanf("%d",&p);  
    for (int i=1;i<=p;++i){  
        scanf("%d%d",&x,&y);  
        if (find(x)!=find(y)){  
            printf("-1\n");continue;  
        }  
        int ans=lca(x,y);printf("%d\n",ans);  
    }  
}  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值