Codeforces 609E (Kruskal求最小生成树+树上倍增求LCA)
题面
传送门
题目大意:
给定一个无向连通带权图G,对于每条边,求包含这条边的生成树大小的最小值
分析
包含这条边的生成树的大小如何表示呢?
先求出整张图的最小生成树大小tlen,对于每一条边,我们最小生成树中去掉树上从u到v的路径上权值最大,最大值为mlen的一条边,再加上w,得到的一定是包含这条边的生成树大小的最小值
最小生成树大小tlen可用kruskal算法在时间内求出
那么问题转化为求mlen,可用树上倍增法求解
树上倍增法的好处是在求LCA的同时可以维护更多的附加信息
在求LCA的过程中设fa[i][j]表示i的辈祖先
可写出公式
(即i的辈祖先是i的辈祖先的辈祖先)
同理可写出最大长度
查询时类似LCA的查询即可,详情见代码
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#define maxn 200005
#define maxm 200005
#define maxlog 32
using namespace std;
int n,m;
inline int qread(){
int x=0,sign=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-') sign=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=x*10+c-'0';
c=getchar();
}
return x*sign;
}
struct edge{
int from;
int to;
int len;
int next;
int index;
edge(){
}
edge(int x,int y,int z,int i){
from=x;
to=y;
len=z;
index=i;
}
friend bool operator <(edge x,edge y){
return x.len<y.len;
}
};
edge G[maxm*2],MST[maxm*2];
int head[maxn];
int size=0;
void add_edge(int u,int v,int w){
size++;
MST[size].from=u;
MST[size].to=v;
MST[size].len=w;
MST[size].next=head[u];
head[u]=size;
}
int fset[maxn];
void set_init(){
for(int i=1;i<=n;i++) fset[i]=i;
}
int find(int x){
if(fset[x]==x) return x;
else{
fset[x]=find(fset[x]);
return fset[x];
}
}
long long kruskal(){
long long ans=0;
sort(G+1,G+1+m);
for(int i=1;i<=m;i++){
int fx=find(G[i].from);
int fy=find(G[i].to);
if(fx!=fy){
add_edge(G[i].from,G[i].to,G[i].len);
add_edge(G[i].to,G[i].from,G[i].len);
fset[fx]=fy;
ans+=G[i].len;
}
}
return ans;
}
int deep[maxn],fa[maxn][maxlog];
long long mlen[maxn][maxlog];
int log2n;
void lca_init(){
queue<int>q;
q.push(1);
deep[1]=1; //初始化深度
while(!q.empty()){
int x=q.front();
q.pop();
for(int i=head[x];i;i=MST[i].next){//MST表示最小生成树的边
int y=MST[i].to;
if(deep[y]) continue;
deep[y]=deep[x]+1;
fa[y][0]=x;//fa和mlen的初始值
mlen[y][0]=MST[i].len;
for(int j=1;j<=log2n;j++){//倍增初始化
fa[y][j]=fa[fa[y][j-1]][j-1];
mlen[y][j]=max(mlen[y][j-1],mlen[fa[y][j-1]][j-1]);
}
q.push(y);
}
}
}
long long lca_query(int x,int y){
if(deep[x]>deep[y]) swap(x,y);
long long maxl=0;
for(int i=log2n;i>=0;i--){//先将x和y调整到同一深度
if(deep[fa[y][i]]>=deep[x]){
maxl=max(maxl,mlen[y][i]);//y上升同时更新maxl
y=fa[y][i];
}
}
if(x==y) return maxl;//如果LCA(x,y)=x,直接返回
for(int i=log2n;i>=0;i--){//x,y同时上升,直到差一条边相遇
if(fa[x][i]!=fa[y][i]){
maxl=max(maxl,max(mlen[x][i],mlen[y][i]));
x=fa[x][i];
y=fa[y][i];
}
}
maxl=max(maxl,max(mlen[x][0],mlen[y][0]));//最后再更新一次
return maxl;
}
long long ans[maxm];//便于按输入顺序输出
int main(){
int s,t,r;
n=qread();
m=qread();
for(int i=1;i<=m;i++){
s=qread();
t=qread();
r=qread();
G[i]=edge(s,t,r,i);
}
set_init();
long long tlen=kruskal();
log2n=log2(n)+1;
lca_init();
for(int i=1;i<=m;i++){
ans[G[i].index]=tlen+(long long)G[i].len-(long long)lca_query(G[i].from,G[i].to);//求生成树大小的最小值
}
for(int i=1;i<=m;i++){
printf("%I64d\n",ans[i]);
}
}
版权声明:因为我是蒟蒻,所以请大佬和神犇们不要转载(有坑)的文章,并指出问题,谢谢