【【模板】严格次小生成树[BJWC2010]】

树上的路径怎么能没有树剖

显然,次小生成树和最小生成树只在一条边上有差距,于是我们就可以枚举这一条边,将所有边加入最小生成树,之后再来从这些并不是那么小的生成树中找到那个最小的

我们往最小生成树里加入一条边一定会在这条边的两个端点之间形成一个环,为了让维持树的结构,我们要断开环上的一条边,而为了让得到的新生成树尽量小,于是我们就选择最大的一条边断开,但是为了保证严格次小,在这条边和最大边长度相同时,断开一条严格次大的边

而从树上找两点之间的最大边和严格次大边,我们显然可以直接上树剖

我们可以维护出每一个到其所在重链顶端的最大边和严格次大边,之后直接倍增往上跳就可以了

直到跳到两个点在同一条重链上的时候,没有办法在像之前那样做了,于是直接用线段树来查询

单次做的复杂度还是\(O(logn)\)

但是树剖是出了名的小常数,常数大的线段树也只做了一次查询,于是效率非常喜人,在我人傻常数大的情况下依旧跑到了最优解第二

代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<bitset>
#define mp std::make_pair
#define re register
#define LL long long
#define maxn 100005
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define k1 first
#define k2 second
typedef std::pair<int,int> pii;
struct node
{
	int v,nxt,w;
}e[maxn<<1],a[maxn*3];
std::bitset<maxn*3> ff;
int l[maxn<<2],r[maxn<<2],t1[maxn<<2],t2[maxn<<2];
int head[maxn],deep[maxn],fa[maxn],tot[maxn],to[maxn],b[maxn];
int top[maxn],f[maxn],son[maxn],sum[maxn],d1[maxn],d2[maxn],pre[maxn];
int n,m,num,_;
LL cnt;
void dfs1(int r)
{
	sum[r]=1;
	int maxx=-1;
	for(re int i=head[r];i;i=e[i].nxt)
	if(!deep[e[i].v])
	{
		deep[e[i].v]=deep[r]+1;
		pre[e[i].v]=pre[r]+e[i].w;
		f[e[i].v]=r;
		dfs1(e[i].v);
		sum[r]+=sum[e[i].v];
		if(sum[e[i].v]>maxx) maxx=sum[e[i].v],son[r]=e[i].v;
	}
}
void dfs2(int r,int topf)
{
	top[r]=topf;
	to[r]=++_;
	b[_]=pre[r]-pre[f[r]];
	if(r!=topf)
	{
		if(pre[r]-pre[f[r]]>d1[f[r]]) d2[r]=d1[f[r]];
			else if(pre[r]-pre[f[r]]<d1[f[r]]) d2[r]=max(d2[r],pre[r]-pre[f[r]]);
				else d2[r]=d2[f[r]];
		d1[r]=max(d1[f[r]],pre[r]-pre[f[r]]);
	}
	if(!son[r]) return;
	dfs2(son[r],topf);
	for(re int i=head[r];i;i=e[i].nxt)
	if(deep[e[i].v]>deep[r]&&son[r]!=e[i].v) d1[e[i].v]=-99,d2[e[i].v]=-100,dfs2(e[i].v,e[i].v);
}
inline int find(int x)
{
	if(fa[x]==x) return x;
	return fa[x]=find(fa[x]);
}
inline int read()
{
	char c=getchar();
	int x=0;
	while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9')
		x=(x<<3)+(x<<1)+c-48,c=getchar();
	return x;
}
inline void add_edge(int x,int y,int z)
{
	e[++num].v=y;
	e[num].w=z;
	e[num].nxt=head[x];
	head[x]=num;
}
inline int cmp(node K,node M)
{
	return K.w<M.w;
}
inline void swap(int &a,int &b) {a^=b,b^=a,a^=b;}
void build(int x,int y,int i)
{
	l[i]=x;r[i]=y;
	if(x==y)
	{
		t1[i]=b[x];
		return;
	}
	int mid=x+y>>1;
	build(x,mid,i<<1),build(mid+1,y,i<<1|1);
	t1[i]=max(t1[i<<1|1],t1[i<<1]);
	if(t1[i<<1|1]>t1[i<<1]) t2[i]=max(t2[i<<1|1],t1[i<<1]);
		else if(t1[i<<1]>t1[i<<1|1]) t2[i]=max(t2[i<<1],t1[i<<1|1]);
			else t2[i]=max(t2[i<<1|1],t2[i<<1]);
}
pii query(int x,int y,int i)
{
	if(x<=l[i]&&y>=r[i]) return mp(t1[i],t2[i]);
	int mid=l[i]+r[i]>>1;
	if(y<=mid) return query(x,y,i<<1);
	if(x>mid) return query(x,y,i<<1|1);
	pii lson=query(x,y,i<<1),rson=query(x,y,i<<1|1);
	pii now;
	now.k1=max(lson.k1,rson.k1);
	if(lson.k1>rson.k1) now.k2=max(lson.k2,rson.k1);
		else if(lson.k1<rson.k1) now.k2=max(lson.k1,rson.k2);
			else now.k2=max(rson.k2,lson.k2);
	return now;
}
int main()
{
	n=read(),m=read();
	for(re int i=1;i<=m;++i)
		a[i].v=read(),a[i].nxt=read(),a[i].w=read();
	std::sort(a+1,a+m+1,cmp);
	for(re int i=1;i<=n;++i) fa[i]=i,tot[i]=1;
	int K=0;
	for(re int i=1;i<=m;++i)
	{
		int xx=find(a[i].v);
		int yy=find(a[i].nxt);
		if(xx!=yy)
		{
			K++;
			ff[i]=1;
			add_edge(a[i].v,a[i].nxt,a[i].w);
			add_edge(a[i].nxt,a[i].v,a[i].w);
			if(tot[xx]>tot[yy]) fa[yy]=xx,tot[xx]+=tot[yy];
			else fa[xx]=yy,tot[yy]+=tot[xx];
			cnt+=a[i].w;
		}
		if(K==n-1) break;
	}
	deep[1]=1;
	dfs1(1);
	d1[1]=-99,d2[1]=-100;
	dfs2(1,1);
	build(1,n,1);
	LL ans=9999999999999999;
	for(re int i=m;i;--i)
	if(!ff[i])
	{
		int m1=0,m2=-1;
		int x=a[i].v;
		int y=a[i].nxt;
		while(top[x]!=top[y])
		{
			if(deep[top[x]]<deep[top[y]]) swap(x,y);
			if(d1[x]<m1) m2=max(m2,d1[x]);
				else if(d1[x]>m1) m2=m1;
					else m2=max(m2,d2[x]);
			m1=max(m1,d1[x]);
			if(pre[x]-pre[f[x]]<m1) m2=max(m2,pre[x]-pre[f[x]]);
				else if(pre[x]-pre[f[x]]>m1) m2=m1;
			m1=max(m1,pre[x]-pre[f[x]]);
			x=f[top[x]];
		}
		if(x!=y)
		{
			if(deep[x]>deep[y]) swap(x,y);
			pii now=query(to[x]+1,to[y],1);
			if(now.k1>m1) m2=m1;
				else if(now.k1<m1) m2=max(m2,now.k1);
					else m2=max(m2,now.k2);
			m1=max(m1,now.k1);
		}
		if(a[i].w<m1) continue;
		if(a[i].w>m1) ans=min(ans,cnt+a[i].w-m1);
		if(a[i].w==m1&&m2) ans=min(ans,cnt+a[i].w-m2);
	}
	std::cout<<ans;
	return 0;
}
posted @ 2019-01-01 21:43  asuldb  阅读(126)  评论(0编辑  收藏  举报