www.cnblogs.com/shaokele/
bzoj 1977: [BeiJing2010组队]次小生成树 Tree##
Time Limit: 10 Sec
Memory Limit: 512 MBDescription###
小 C 最近学了很多最小生成树的算法,Prim 算法、Kurskal 算法、消圈算法等等。 正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了。小 P 说,让小 C 求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说: 如果最小生成树选择的边集是 EM,严格次小生成树选择的边集是 ES,那么需要满足:(value(e) 表示边 e的权值)
这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。
Input###
第一行包含两个整数N 和M,表示无向图的点数与边数。 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z。
Output###
包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)
Sample Input###
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≤2 000 M≤3 000; 80% 的数据N≤50 000 M≤100 000; 100% 的数据N≤100 000 M≤300 000 ,边权值非负且不超过 10^9 。
题目地址: bzoj 1977: [BeiJing2010组队]次小生成树 Tree
题目大意: 求严格次小的生成树
题解:
水题
md写了两个小时
先求出最⼩⽣成树
对于每条不在最小生成树的边(u,v,w),求出最⼩⽣成树中,u,v之间最⼤值,若最⼤值等于w,则找严格次⼤值
最⼤值与次⼤值,都可以通过树上倍增来处理
数据下载 bzoj 1977
AC代码
#include <cstdio>
#include <algorithm>
#define ll long long
using namespace std;
const int N=1e5+5,M=3e5+5;
int n,m,cnt,stmax,ndmax;
int f[N],last[N],dep[N],fa[N][18],g[N][18],G[N][18];
ll res;
bool vis[N],flag[M];
struct edge{
int u,v,w;
}e[M];
struct star{
int u,v,w,next;
}E[N+N];
void add_edge(int u,int v,int w){
E[++cnt]=(star){u,v,w,last[u]};last[u]=cnt;
E[++cnt]=(star){v,u,w,last[v]};last[v]=cnt;
}
int find(int x){
while(x!=f[x])x=f[x]=f[f[x]];
return x;
}
bool cmp(edge a,edge b){
return a.w<b.w;
}
void Kurskal(){
int cnt=0;
sort(e+1,e+m+1,cmp);
for(int i=1;i<=m;i++){
int fu=find(e[i].u);
int fv=find(e[i].v);
if(fu==fv)continue;
flag[i]=1;
add_edge(e[i].u,e[i].v,e[i].w);
res+=e[i].w;
f[fv]=fu;
if(++cnt==n-1)return;
}
}
void dfs(int u,int father){
if(vis[u])return;vis[u]=1;
dep[u]=dep[father]+1;
for(int i=last[u];i;i=E[i].next){
int v=E[i].v;
dfs(v,u);
fa[v][0]=u;
g[v][0]=E[i].w;
G[v][0]=-1;
}
}
int lca(int a,int b){
if(dep[a]<dep[b])swap(a,b);
int t=dep[a]-dep[b];
for(int i=17;i>=0;i--)
if((1<<i)&t)a=fa[a][i];
for(int i=17;i>=0;i--)
if(fa[a][i]!=fa[b][i])
a=fa[a][i],b=fa[b][i];
if(a==b)return a;
return fa[a][0];
}
void calc(int a,int b){
int t=dep[a]-dep[b];
for(int i=17;i>=0;i--)
if((1<<i)&t){
if(g[a][i]>stmax){
ndmax=stmax;
stmax=g[a][i];
}
ndmax=max(ndmax,G[a][i]);
a=fa[a][i];
}
}
void solve(int a,int b,int top){
calc(a,top);calc(b,top);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
f[i]=i;
for(int i=1;i<=m;i++)
scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
Kurskal();
dfs(1,0);
for(int j=1;j<=17;j++)
for(int i=1;i<=n;i++){
fa[i][j]=fa[fa[i][j-1]][j-1];
g[i][j]=max(g[i][j-1],g[fa[i][j-1]][j-1]);
if(g[i][j-1]!=g[fa[i][j-1]][j-1])
G[i][j]=min(g[i][j-1],g[fa[i][j-1]][j-1]);
G[i][j]=max(G[i][j],max(G[i][j-1],G[fa[i][j-1]][j-1]));
}
ll ans=1e14+5;
for(int i=1;i<=m;i++)
if(!flag[i]){
int top=lca(e[i].u,e[i].v);
stmax=ndmax=-1;
solve(e[i].u,e[i].v,top);
if(stmax!=e[i].w)
ans=min(ans,res-stmax+e[i].w);
else
ans=min(ans,res-ndmax+e[i].w);
}
printf("%lld
",ans);
return 0;
}