Problem I – Imperial roads(最小生成树每次固定一条边)

原题: http://codeforces.com/gym/101889/status

题意:

1e5次询问,每次给出一条必须连的路,求最小生成树

解析:

首先先做一遍最小生成树,记录此时的花费以及树的结构

(注意,虽然算法里面连接的是两个fa,但是实际上连的是这条边的两个点)

然后,对于每次询问的两个点,查看是否以及连接。如果是那么就输出之前记下的花费

如果不是,说明这条边需要连上去,而这条边和这两个点原来的路径会形成一个环,我们需要在这个环中删除一条边使整个数据结构变回树。相当于找出原来的树中两个点到LCA之间的花费最大的一条边

如果暴力到LCA的路径会炸,我们可以用倍增来预处理这一条路上的MAX

#include<bits/stdc++.h>
using namespace std;
#define debug(x) printf("# %d\n",x)
#define pill pair<int,int>
map<pill,int>dis;//记录所有路的长度
int n,m;
const int N=1e5+5;
int head[N],to[4*N],nex[4*N],v[4*N];int now;//链式前向星
void add(int a,int b,int V){
    nex[++now]=head[a];head[a]=now;to[now]=b;v[now]=V;
    nex[++now]=head[b];head[b]=now;to[now]=a;v[now]=V;
}

int fa[N];//并查集
int fin(int a){return fa[a]==a?a:fa[a]=fin(fa[a]); }

struct ed{//最小生成树
    int a,b,v;
    bool operator < (const ed r)const{
        return v<r.v;
    }
    ed(int a,int b,int v):a(a),b(b),v(v) {}
};
vector<ed>V;
vector<int>link[N];//记录最后的树形结构中,所有的边
int MinTree(){//最小生成树
    sort(V.begin(),V.end());
    int Sum=0;
    for(int i=0;i<V.size();i++){
        int F1=fin(V[i].a),F2=fin(V[i].b);
        if(F1!=F2){
            fa[F1]=F2;
            link[V[i].a].push_back(V[i].b);
            link[V[i].b].push_back(V[i].a);
            Sum+=V[i].v;
        }
    }
    return Sum;
}
int p[N][30];int deep[N];//2的i次祖先 深度
int valToFa[N][30];//到2的i次祖先的最长路
void InitFa(int p,int f){
    if(f==0)memset(fa,0,sizeof(fa));//需要初始化fa数组
    for(int i=0;i<link[p].size();i++){
        int u=link[p][i];
        if(u==f)continue;
        deep[u]=deep[p]+1;
        valToFa[u][0]=dis[{u,p}];
        fa[u]=p;
        InitFa(u,p);
    }
}
void InitLca(){//预处理倍增
    InitFa(fin(1),0);
    for(int i=1;i<=n;i++)p[i][0]=fa[i];

    for(int i=1;;i++){
        bool Finish=1;
        for(int j=1;j<=n;j++){
            if(p[j][i-1])
                Finish=0, p[j][i]=p[p[j][i-1]][i-1],
                valToFa[j][i]=max(valToFa[j][i-1],valToFa[p[j][i-1]][i-1]);
        }
        if(Finish)break;
    }
}
int LCA(int a,int b){//LCA
    if(deep[a]>deep[b])swap(a,b);
    int i;
    for(i=0;(1<<i)<deep[b];i++);i--;
    for(;;){
        if(deep[a]==deep[b])break;
        if(deep[b]-deep[a]<(1<<i))i--;
        else b=p[b][i];
    }
    for(i=0;(1<<i)<deep[b];i++);i--;
    for(;;){
        if(a==b)return a;
        if(fa[a]==fa[b])return fa[a];
        if(p[a][i]==p[b][i])i--;
        else a=p[a][i],b=p[b][i];
    }
}
int main(){
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)fa[i]=i;
    while(m--){
        int a,b,v;
        scanf("%d%d%d",&a,&b,&v);
        add(a,b,v);dis[{a,b}]=v;dis[{b,a}]=v;
        V.push_back(ed(a,b,v));
    }
    int Money=MinTree();
    InitLca();
    int q;scanf("%d",&q);
    while(q--){
        int a,b;scanf("%d%d",&a,&b);
        if(fa[a]==b||fa[b]==a)printf("%d\n",Money);//已经连上
        else{
            int G=LCA(a,b);int Dis=dis[{a,b}];
            int Max=-1;
            while(a!=G){
                int ba=1;int co=0;
                int sub=-deep[G]+deep[a];
                while(ba<=sub)ba<<=1,co++;
                co--;
                Max=max(Max,valToFa[a][co]);
                a=p[a][co];
            }
            a=b;
            while(a!=G){
                int ba=1;int co=0;
                int sub=-deep[G]+deep[a];
                while(ba<=sub)ba<<=1,co++;
                co--;
                Max=max(Max,valToFa[a][co]);
                a=p[a][co];
            }
            printf("%d\n",Money-Max+Dis);
        }
    }
}




猜你喜欢

转载自blog.csdn.net/jk_chen_acmer/article/details/83096643