洛谷1967货车运输

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

倍增LCA裸题。用了在线。还有离线O(n)做法、树链剖分做法,暂不管了。

(自己程序的)坑点:1.xnt从1开始!2.数组大小!!!

重边在最大生成树的时候就解决啦~

  不然我就要进了子节点的dfs以后遍历一遍它的边,从与fa相连者中找出最大的作为mn[ ][0]的值;用vis标记使fa再走到自己时不进dfs了。

值得注意的地方是它可能是一个森林。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=1e5+5,M=5e5+5,lm=17;const ll INF=0x7fffffff;
int n,m,head[N],xnt=1,dep[N],pre[N][lm+5],q;
ll mn[N][lm+5],fa[N];
bool use[M<<1],in[N];
struct Edge{
    int next,from,to,bh;ll w;
    Edge(int n=0,int f=0,int t=0,int b=0,ll w=0):next(n),from(f),to(t),bh(b),w(w) {}
}edge[M<<1],tp[N<<1];
bool cmp(Edge a,Edge b){return a.w>b.w;}
int find(int a)
{
    if(fa[a]==a)return a;
    return fa[a]=find(fa[a]);
}
void kruskal()
{
    memcpy(tp,edge,sizeof tp);
    sort(tp+1,tp+xnt+1,cmp);
    for(int i=1,u,v,k;i<=xnt;i++)
        if(find(u=tp[i].from)!=find(v=tp[i].to))
            fa[find(u)]=find(v),use[k=tp[i].bh]=use[k^1]=1;
}
void dfs(int cur,int fa)
{
    pre[cur][0]=fa;dep[cur]=dep[fa]+1;
    for(int i=1;i<=lm;i++)
    {
        int k=pre[cur][i-1];//不是i>>1 
        if(!pre[k][i-1])break;
        pre[cur][i]=pre[k][i-1];
        mn[cur][i]=min(mn[cur][i-1],mn[k][i-1]);//不用赋初值了 
    }
    for(int i=head[cur],v;i;i=edge[i].next)
        if(use[i]&&(v=edge[i].to)!=fa)mn[v][0]=edge[i].w,dfs(v,cur);
}
ll lca(int x,int y)
{
    ll ans=INF;
    if(dep[x]<dep[y])swap(x,y);
    int d=dep[x]-dep[y];
    for(int i=lm;i>=0;i--)//
        if(d&(1<<i)){ans=min(ans,mn[x][i]);x=pre[x][i];}
    if(x==y)return ans;//
    for(int i=lm;i>=0;i--)
        if(pre[x][i]!=pre[y][i])
        {ans=min(ans,min(mn[x][i],mn[y][i]));x=pre[x][i];y=pre[y][i];}
    return min(ans,min(mn[x][0],mn[y][0]));
}
int main()
{
    scanf("%d%d",&n,&m);int x,y;ll z;
    for(int i=1;i<=n;i++)fa[i]=i;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%lld",&x,&y,&z);
        edge[++xnt]=Edge(head[x],x,y,xnt,z);head[x]=xnt;
        edge[++xnt]=Edge(head[y],y,x,xnt,z);head[y]=xnt;
    }
    kruskal();
    for(int i=1;i<=n;i++)
        if(fa[i]==i)dfs(i,0);//森林 
//    for(int j=1;j<=lm;j++)
//        for(int i=1;i<=n;i++)
//            {
//                mn[i][j]=min(mn[pre[i][j-1]][j-1],mn[i][j-1]);
//                pre[i][j]=pre[pre[i][j-1]][j-1];
//            }
    scanf("%d",&q);
    for(int i=1;i<=q;i++)
    {
        scanf("%d%d",&x,&y);
        if(find(x)!=find(y))printf("-1\n");
        else printf("%lld\n",lca(x,y));
    }
}

 

posted on 2018-04-19 23:00  Narh  阅读(146)  评论(0编辑  收藏  举报

导航