原题: 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);
}
}
}