bzoj3732 Network 最小生成树+LCA+树上倍增

这道题不就是Noip2013的t3吗…没想到bzoj换了个包装.noip2013货车运输是最大生成树,这里是最小生成树.
因为要是最大的边最小,则一定存在于最小生成树中.我们只需要跑一遍kruskal再树上倍增求LCA的同时求最大值即可.

注意:代码中Min其实求的是最大值,因为我直接从之前noip2013粘过来的,所以这里偷懒一下只改一下符号没改名称.

#include<stdio.h>
#include<algorithm>
const int maxn=100001;
const int Uplimit=16;
int aa,bb,tot,num,ans,h[maxn],fa[maxn],anc[maxn][Uplimit],vmin[maxn][Uplimit],n,m,dep[maxn],q;
inline int Min(int x,int y){ return x>y?x:y;}
struct optimus{
    int x,y,z;
}prime[maxn];
struct edge{
    int nxt,v,w;
}e[maxn*2];
inline void add(int u,int v,int w){
    e[++num].v=v,e[num].w=w,e[num].nxt=h[u],h[u]=num;
    e[++num].v=u,e[num].w=w,e[num].nxt=h[v],h[v]=num;
}
inline bool cmp(optimus a,optimus b){return a.z<b.z;}
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read(){
    register int x=0;
    register char ch=nc();
    while(ch<'0'||ch>'9')ch=nc();
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=nc();
    return x;
}
int I_Just_Wanna_Run(int x){return x==fa[x]?x:fa[x]=I_Just_Wanna_Run(fa[x]);}
inline void kruskal(){
    std::sort(prime+1,prime+m+1,cmp);
    for(register int i=1;i<=n;i++) fa[i]=i;
    for(register int i=1;i<=m;i++){
        aa=I_Just_Wanna_Run(prime[i].x),bb=I_Just_Wanna_Run(prime[i].y);
        if(aa!=bb){
           ++tot;
           fa[aa]=bb;
           add(prime[i].x,prime[i].y,prime[i].z);
           if(tot==n-1) break;
        }
    }
}
void dfs(int u,int father,int value){
    dep[u]=dep[father]+1;
    anc[u][0]=father,vmin[u][0]=value;
    for(int p=1;p<Uplimit;p++) anc[u][p]=anc[anc[u][p-1]][p-1],vmin[u][p]=Min(vmin[u][p-1],vmin[anc[u][p-1]][p-1]);
    for(int i=h[u];i;i=e[i].nxt){
        int v=e[i].v;
        if(v==father) continue;
        dfs(v,u,e[i].w);
    }
}
int query(int u,int v){
    ans=0;
    if(dep[u]<dep[v]) std::swap(u,v);
    int D_depth=dep[u]-dep[v];
    for(int p=0;D_depth;D_depth>>=1,p++)
     if(D_depth&1)ans=Min(ans,vmin[u][p]),u=anc[u][p];
    if(u==v) return ans;
    for(int p=Uplimit-1;p>=0;p--)
     if(anc[u][p]!=anc[v][p])
       ans=Min(ans,Min(vmin[u][p],vmin[v][p])),u=anc[u][p],v=anc[v][p];
    return ans=Min(ans,Min(vmin[u][0],vmin[v][0]));
}
int main(){
    n=read(),m=read();q=read();
    for(register int i=1;i<=m;i++) prime[i].x=read(),prime[i].y=read(),prime[i].z=read();
    kruskal();
    for(register int i=1;i<=n;i++) if(!dep[i]) dfs(i,i,0);
    while(q--){
      int x=read(),y=read();
      if(I_Just_Wanna_Run(x)!=I_Just_Wanna_Run(y)) printf("-1\n");
      else printf("%d\n",query(x,y));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值