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

这题果然是模板题

一堆做法

但是根本思想是一样的

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

主要的实现方法有三种

1.kruskal+倍增+lca

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

2.kruskal+lct

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

3.kruskal+树剖

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

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

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

那么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=max(max(seg[lc].__max,seg[rc].__max),min(seg[lc]._max,seg[rc]._max));	
}

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

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

如果不等,最大值就是左右儿子中最大值较大的那个,次大值就是左右儿子中最大值较小的那个,或者是两个次大值中更大的那个,这两种情况去一个max(一定要注意取max要不然虽然能A但是是错的

维护好这个之后,我们每次尝试加进去一条不是最小生成树上的边,这样会形成一个环,然后如果环上最大的那条边 ≥ \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=max(max(seg[lc].__max,seg[rc].__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=max(max(l.second,r.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+

update 2020.2.29 13:57 这题数据有点水,开始pushup写错了居然还A了

一定要注意考虑次大的也可能是最大的

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值