bzoj 1977: [BeiJing2010组队]次小生成树 Tree

www.cnblogs.com/shaokele/


bzoj 1977: [BeiJing2010组队]次小生成树 Tree##

  Time Limit: 10 Sec
  Memory Limit: 512 MB

Description###

  小 C 最近学了很多最小生成树的算法,Prim 算法、Kurskal 算法、消圈算法等等。 正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了。小 P 说,让小 C 求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说: 如果最小生成树选择的边集是 EM,严格次小生成树选择的边集是 ES,那么需要满足:(value(e) 表示边 e的权值)
  p1
  这下小 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\n",ans);
	return 0;
}
posted @ 2018-07-19 16:27  skl_win  阅读(217)  评论(0编辑  收藏  举报
Live2D