[BJWC2010] 严格次小生成树(kruskal+树剖)

这题果然是模板题

一堆做法

但是根本思想是一样的

都是先跑一遍最小生成树,然后维护一下路径上最大值和小于最大值的最大值

主要的实现方法有三种

1.kruskal+倍增+lca

复杂度是O(mlogm)O(mlogm)O(mlogm),优点是复杂度低,常熟不是特别大,代码短,缺点是实现细节多

2.kruskal+lct

复杂度还是O(mlogm)O(mlogm)O(mlogm),优点是复杂度低,代码长度较短,缺点是常熟大,实现细节多

3.kruskal+树剖

复杂度是O(mlog2n)O(mlog^2n)O(mlog2n),优点是实现简单(只用考虑左右儿子的合并),细节少,(在同复杂度水平下)常熟小,缺点是复杂度有点高,代码长度大,但是这道题10510^5105级别的数据复杂度是正确的

所以我们主要来说一下这个树剖的做法(自认为是最稳的一种)

先跑一遍最小生成树,然后线段树维护最小生成树上的区间最大值和区间小于最大值的最大值

那么pushup大概是这个样子的

void pushup(int u){
	if(seg[lc]._max==seg[rc]._max)seg[u]._max=seg[lc]._max,seg[u].__max=max(seg[lc].__max,seg[rc].__max);
	else seg[u]._max=max(seg[lc]._max,seg[rc]._max),seg[u].__max=min(seg[lc]._max,seg[rc]._max);	
}

其中_max是最大值,__max是小于最大值的最大是变量名毒瘤勿喷

解释一下就是如果左右儿子的最大值相等,那么最大值就是这个值,次大值就是左右儿子的次大值中更大的那个

如果不等,最大值就是左右儿子中较大的那个,次大值就是左右儿子中较小的那个

维护好这个之后,我们每次尝试加进去一条不是最小生成树上的边,这样会形成一个环,然后如果环上最大的那条边\geq≥要加进去的这条边就看第二大能不能加,然后算出来这条边加进去之后的最小生成树,最后统一取个min就可以了

几个问题

1.要开longlong,ans初值要大一点,否则wa#8

2.题目只说保证没有重边,没说保证没有自环,所以判断一下,否则wa#10

3.数组开的稍微大一点,否则re#10qwq

代码:

# include <bits/stdc++.h>
using namespace std;

# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)
# define debug puts("QAQ");

typedef long long ll;
const int N=2e5+5;
const int mod=1e9+7;
const double eps=1e-7;

template <typename T> void read(T &x){
	x=0;int f=1;
	char c=getchar();
	for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
	for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
	x*=f;
}

# define int long long

int n,m;
int head[N],cnt;
int faz[N],son[N],dep[N],siz[N],top[N],dfn[N],tot;
int sum,ans=1e18;
int val[N],_a[N];
int f[N];
bool vis[N];

struct Edge{
	int to,next,w;	
}e[N<<1];

void add(int x,int y,int c){
	e[++cnt]=(Edge){y,head[x],c},head[x]=cnt;	
}

struct edge{
	int x,y,c;
	bool operator < (const edge &cmp)const{
		return c<cmp.c;
	}
}a[N<<2];

int find(int x){
	if(f[x]==x)return x;
	return f[x]=find(f[x]);	
}

struct segment_tree{
	int l,r;
	int _max,__max;	
}seg[N<<2];

# define lc (u<<1)
# define rc (u<<1|1)

void pushup(int u){
	if(seg[lc]._max==seg[rc]._max)seg[u]._max=seg[lc]._max,seg[u].__max=max(seg[lc].__max,seg[rc].__max);
	else seg[u]._max=max(seg[lc]._max,seg[rc]._max),seg[u].__max=min(seg[lc]._max,seg[rc]._max);	
}

pair<int,int> merge(pair<int,int> l,pair<int,int> r){
	pair<int,int> res;
	if(l.first==r.first)res.first=l.first,res.second=max(l.second,r.second);
	else res.first=max(l.first,r.first),res.second=min(l.first,r.first);
	return res;	
}

void build(int u,int l,int r){
	seg[u].l=l,seg[u].r=r;
	if(l==r){seg[u]._max=_a[l];return;}
	int mid=l+r>>1;
	build(lc,l,mid);
	build(rc,mid+1,r);
	pushup(u);
}

pair<int,int> query(int u,int l,int r){
	if(seg[u].l>=l&&seg[u].r<=r)return make_pair(seg[u]._max,seg[u].__max);
	int mid=seg[u].l+seg[u].r>>1;
	if(r<=mid)return query(lc,l,r);
	if(l>mid)return query(rc,l,r);
	return merge(query(lc,l,r),query(rc,l,r));	
}

pair<int,int> RouteQuery(int x,int y){
	pair<int,int> res;
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]])swap(x,y);
		res=merge(res,query(1,dfn[top[x]],dfn[x]));
		x=faz[top[x]];
	}
	if(x!=y){
		if(dep[x]>dep[y])swap(x,y);
		res=merge(res,query(1,dfn[x]+1,dfn[y]));	
	}
	return res;
}

void dfs1(int u,int fa){
	faz[u]=fa;
	siz[u]=1;
	dep[u]=dep[fa]+1;
	RepG(i,u){
		int v=e[i].to;
		if(v==fa)continue;
		val[v]=e[i].w;
		dfs1(v,u);
		siz[u]+=siz[v];
		if(siz[v]>siz[son[u]])son[u]=v;	
	}
}

void dfs2(int u,int _top){
	top[u]=_top;
	dfn[u]=++tot;
	_a[tot]=val[u];	
	if(!son[u])return;
	dfs2(son[u],_top);
	RepG(i,u){
		int v=e[i].to;
		if(v==faz[u]||v==son[u])continue;
		dfs2(v,v);	
	}
}

void kruskal(){
	int tott=0;
	sort(a+1,a+m+1);
	Rep(i,1,m){
		int fax=find(a[i].x),fay=find(a[i].y);
		if(fax==fay)continue;
		vis[i]=true;
		f[fax]=fay;
		add(a[i].x,a[i].y,a[i].c),add(a[i].y,a[i].x,a[i].c);
		sum+=a[i].c;
		if(++tott==n-1)break;
	}
}

signed main()
{
	memset(head,-1,sizeof(head));
	read(n),read(m);
	Rep(i,1,n)f[i]=i;
	Rep(i,1,m){
		read(a[i].x),read(a[i].y),read(a[i].c);
		if(a[i].x==a[i].y)vis[i]=true;
	}
	kruskal();	
	dfs1(1,0),dfs2(1,1);
	build(1,1,n);
	Rep(i,1,m){
		if(vis[i])continue;
		int x=a[i].x,y=a[i].y,c=a[i].c;
		pair<int,int> res=RouteQuery(x,y);
		if(res.first<c)ans=min(ans,sum+c-res.first);
		else if(res.second&&res.second<c)ans=min(ans,sum+c-res.second);
	}
	printf("%lld\n",ans);
	return 0;
}

因为没有修改问题所以代码也不是很长,160+

[BJWC2010] 严格次小生成树(kruskal+树剖)[BJWC2010] 严格次小生成树(kruskal+树剖) devout_ 发布了45 篇原创文章 · 获赞 53 · 访问量 1万+ 私信 关注
上一篇:自适应辛普森积分


下一篇:cf#828