【BJOI2010】次小生成树
【BJOI2010】次小生成树
Description
小C最近学了很多最小生成树的算法,Prim算法、Kurskal算法、消圈算法等等。
正当小C洋洋得意之时,小P又来泼小C冷水了。小P说,让小C求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说:
如果最小生成树选择的边集是EM,严格次小生成树选择的边集是ES,那么需要满足:(value(e)表示边e的权值)
这下小C蒙了,他找到了你,希望你帮他解决这个问题。
正当小C洋洋得意之时,小P又来泼小C冷水了。小P说,让小C求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说:
如果最小生成树选择的边集是EM,严格次小生成树选择的边集是ES,那么需要满足:(value(e)表示边e的权值)
这下小C蒙了,他找到了你,希望你帮他解决这个问题。
Input
输入文件tree.in第一行包含两个整数N和M,表示无向图的点数与边数。接下来M行,每行3个数x,y,z表示,点x和点 y之间有一条边,边的权值为z。
Output
输出文件 tree.out 包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)
Sample Input
5 6
1 2 1
1 3 2
2 4 3
3 5 4
3 4 3
4 5 6
1 2 1
1 3 2
2 4 3
3 5 4
3 4 3
4 5 6
Sample Output
11
Hint
【数据规模】
数据中无向图无自环;
50%的数据 N≤2000 M≤3000;
80%的数据 N≤50000 M≤100000;
100%的数据 N≤100000 M≤300000,边权值非负且不超过 10^9。
数据中无向图无自环;
50%的数据 N≤2000 M≤3000;
80%的数据 N≤50000 M≤100000;
100%的数据 N≤100000 M≤300000,边权值非负且不超过 10^9。
Solution
题面很清晰,求严格的次小生成树。
如果是简单的最小生成树,那么用Prim或者Kruskal都可以解决。
如果只是求次小生成树的话,枚举每一条没有被选中的边(x,y),查询x->y的路径中的最大边,连接xy之后删除
最大边就可以得到次小的生成树的一种可能性,选取最后值最小的一个就是次小生成树。
但是在求严格次小生成树的时候,如果x到y的路径上的最大边和枚举的边相同时,所得生成树不是严格次小的。所以我们还要记录一下路径上的次大边来解决这种问题。
维护路径最大和次大值。。。蒟蒻忘了(不会)LCA,所以用了树剖。。。
如果是简单的最小生成树,那么用Prim或者Kruskal都可以解决。
如果只是求次小生成树的话,枚举每一条没有被选中的边(x,y),查询x->y的路径中的最大边,连接xy之后删除
最大边就可以得到次小的生成树的一种可能性,选取最后值最小的一个就是次小生成树。
但是在求严格次小生成树的时候,如果x到y的路径上的最大边和枚举的边相同时,所得生成树不是严格次小的。所以我们还要记录一下路径上的次大边来解决这种问题。
维护路径最大和次大值。。。蒟蒻忘了
CODE
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
inline int read(){
char c;int rec=0;
while((c=getchar())<'0'||c>'9');
while(c>='0'&&c<='9')rec=rec*10+c-'0',c=getchar();
return rec;
}
int n,m;
long long ans=0;
struct Edge {int a,b,val,flag;}edge[300005];
inline bool cmp(Edge x,Edge y){return x.val<y.val;}
int pre[100005];
inline int getfa(int x){
if(x==pre[x])return x;
return pre[x]=getfa(pre[x]);
}
struct Branch {int next,to,val;}branch[200005];
int h[100005],cnt=0;
inline void add(int x,int y,int z){
branch[++cnt].to=y;branch[cnt].next=h[x];h[x]=cnt;branch[cnt].val=z;return ;
}
inline void Kruskal(){
int k=0;
for(int i=1;i<=m;i++){
int x=edge[i].a,y=edge[i].b,z=edge[i].val;
int fx=getfa(x),fy=getfa(y);
if(fx==fy)continue;
else {
edge[i].flag=1;
pre[fx]=fy;
k++;ans+=z;
add(x,y,z);add(y,x,z);
}
if(k==n-1)break;
}return ;
}
int len[100005];
int size[100005],deep[100005];
int top[100005],fa[100005],son[100005];
int id[100005],pos[100005];
inline void Dfs1(int v,int pre,int dep){
size[v]=1;deep[v]=dep;fa[v]=pre;
for(int i=h[v];i;i=branch[i].next){
int j=branch[i].to;
if(j==pre)continue;
Dfs1(j,v,dep+1);
size[v]+=size[j];
len[j]=branch[i].val;
if(size[son[v]]<size[j])son[v]=j;
}return ;
}
inline void Dfs2(int v,int T){
top[v]=T;id[v]=++cnt;pos[cnt]=v;
if(son[v])Dfs2(son[v],T);
for(int i=h[v];i;i=branch[i].next){
int j=branch[i].to;
if(j==fa[v]||j==son[v])continue;
Dfs2(j,j);
}return ;
}
struct Seg_Tree{
int L,R,maxx,semx;
}tree[100000<<2];
inline void Build(int v,int L,int R){
tree[v].L=L;tree[v].R=R;
if(L==R){tree[v].maxx=len[pos[L]];return ;}
int mid=(L+R)>>1;
Build(v<<1,L,mid);Build(v<<1|1,mid+1,R);
int f1,f2,s1,s2;
f1=tree[v<<1].maxx;f2=tree[v<<1|1].maxx;
s1=tree[v<<1].semx;s2=tree[v<<1|1].semx;
tree[v].maxx=max(f1,f2);
if(f1>f2){
if(s1>f2)tree[v].semx=s1;
else tree[v].semx=f2;
}
if(f1==f2)tree[v].semx=max(s1,s2);
if(f1<f2){
if(s2>f1)tree[v].semx=s2;
else tree[v].semx=f1;
}
return ;
}
int q1,q2;
inline void Updata(int v){
int f1=tree[v].maxx,f2=tree[v].semx;
if(f1>q1){q2=max(q1,f2);q1=f1;}
if(f1==q1)q2=max(q2,f2);
if(f1<q1)q2=max(q2,f1);
return ;
}
inline void Query(int v,int L,int R){
if(tree[v].L>R||tree[v].R<L)return ;
if(tree[v].L>=L&&tree[v].R<=R){Updata(v);return ;}
Query(v<<1,L,R);Query(v<<1|1,L,R);
}
inline int Lca(int x,int y){
while(top[x]!=top[y]){
if(deep[top[x]]<deep[top[y]])swap(x,y);
x=fa[top[x]];
}if(deep[x]<deep[y])swap(x,y);
return y;
}
int delta=0x3f3f3f3f;
inline void Sov(int v){
int x=edge[v].a,y=edge[v].b,z=edge[v].val;
int p=Lca(x,y);q1=q2=0;
int maxx=0;
while(top[x]!=top[p]){
Query(1,id[top[x]],id[x]);
x=fa[top[x]];
}if(deep[p]+1<=deep[x])Query(1,id[son[p]],id[x]);
while(top[y]!=top[p]){
Query(1,id[top[y]],id[y]);
y=fa[top[y]];
}if(deep[p]+1<=deep[y])Query(1,id[son[p]],id[y]);
if(q1==z)delta=min(z-q2,delta);
else delta=min(z-q1,delta);
return ;
}
int main(){
n=read();m=read();
for(int i=1;i<=m;i++)
{edge[i].a=read();edge[i].b=read();edge[i].val=read();edge[i].flag=0;}
sort(edge+1,edge+1+m,cmp);
for(int i=1;i<=n;i++)pre[i]=i;
Kruskal();cnt=0;
Dfs1(1,0,1);Dfs2(1,1);
Build(1,2,n);
for(int i=1;i<=m;i++)
if(!edge[i].flag)Sov(i);
cout<<ans+delta;
return 0;
}
树剖维护边权的时候求一下LCA,最后判断一下,是否该加上最后一段,而不会多加上额外的边权。