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

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1911

思路:这题是严格次小生成树

首先考虑不严格的次小生成树

我们先求出最小生成树

然后对每条不在最小生成树的边(x,y),求出x->y路径上的最大的边,把它替换这条边之后的树就可能是次小生成树

用倍增思想记录max[x][i]表示x的第2^i的祖先到x的边上的最大值就可以做到O(nlogn)

严格次小稍微有些区别,如果路径最大边和边(x,y)权值相同,就不能替换,而要换严格次大的边

所以多记一个secmax[x][i]表示x的第2^i的祖先到x的边上的严格最大值即可

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int maxn=100010,maxm=300010,maxk=22;
using namespace std;
typedef long long ll;
int n,m,pre[maxm],now[maxn],son[maxm],val[maxm],delta=(int)1e9+7,tot;ll ans;
int fa[maxn][maxk],fim[maxn][maxk],sem[maxn][maxk],dep[maxn],f[maxn];bool in[maxm];
struct Edge{int x,y,v;}E[maxm];
bool operator <(Edge a,Edge b){return a.v<b.v;} 
void add(int a,int b,int c){pre[++tot]=now[a],now[a]=tot,son[tot]=b,val[tot]=c;}
int getfa(int x){return f[x]==x?x:f[x]=getfa(f[x]);}
void dfs(int x){
	for (int i=1;i<=18;i++){
		fa[x][i]=fa[fa[x][i-1]][i-1];
		int t1=fim[x][i-1],t2=fim[fa[x][i-1]][i-1];
		fim[x][i]=max(t1,t2);
		sem[x][i]=max(sem[x][i-1],sem[fa[x][i-1]][i-1]);
		if (t1!=t2) sem[x][i]=max(sem[x][i],min(t1,t2));
	}
	for (int y=now[x];y;y=pre[y]) if (son[y]!=fa[x][0]){
		dep[son[y]]=dep[x]+1,fa[son[y]][0]=x;
		fim[son[y]][0]=val[y],dfs(son[y]);
	}
}

void kruskal(){
	sort(E+1,E+1+m);int cnt=0;
	for (int i=1;i<=n;i++) f[i]=i;
	for (int i=1;i<=m&&cnt<n-1;i++){
		int x=E[i].x,y=E[i].y,v=E[i].v;
		if (getfa(x)==getfa(y)) continue;
		cnt++,f[getfa(x)]=getfa(y),ans+=v,in[i]=1;
		add(x,y,v),add(y,x,v);
	}
}

int lca(int x,int y){
	if (dep[x]<dep[y]) swap(x,y);
	for (int h=dep[x]-dep[y],i=18;i>=0&&h;i--) if (h&(1<<i)) x=fa[x][i];
	if (x==y) return x;
	for (int i=18;i>=0;i--)
		if (fa[x][i]!=fa[y][i])
			x=fa[x][i],y=fa[y][i];
	return fa[x][0];
}

void query(int x,int u,int v){
	int max1=0,max2=0;
	for (int i=18,h=dep[x]-dep[u];i>=0;i--) if (h&(1<<i)){
		if (fim[x][i]>max1) max2=max1,max1=fim[x][i];
		max2=max(max2,sem[x][i]),h-=(1<<i);
	}
	if (v==max1) delta=min(delta,v-max2);
	else delta=min(delta,v-max1);
}

void solve(int id){
	int x=E[id].x,y=E[id].y,v=E[id].v,u=lca(x,y);
	query(x,u,v),query(y,u,v);
}

int main(){
	scanf("%d%d",&n,&m);
	for (int i=1,x,y,z;i<=m;i++) scanf("%d%d%d",&x,&y,&z),E[i]=(Edge){x,y,z};
	kruskal(),dfs(1);
	for (int i=1;i<=m;i++) if (!in[i]) solve(i);
	printf("%lld\n",ans+delta);
	return 0;
}


posted @ 2016-01-12 14:24  orzpps  阅读(135)  评论(0编辑  收藏  举报