P5934 [清华集训2012]最小生成树 题解

发布时间 2023-10-12 15:25:44作者: _hjy

考虑 kruskal 算法的过程。

先将边按边权排序,考虑当加入 \((u,v)\) 时只有 \((u,v)\) 不联通才可能使得其出现在最小生成树中,所以对于所有的边权小于 \(L\) 的边,我们希望去除尽可能少的边使得 \((u,v)\) 不联通。这显然是一个网络流模型。对于每一条边 \((x,y)\) 建立一条容量为 \(1\) 的边,最小割即为答案。

最大生成树的过程是类似的,加入所有边权大于 \(L\) 的边跑最小割,将两次网络流的答案加起来即可。

复杂度分析:
由于容量均为 \(1\),所以在使用 Dinic 算法时间复杂度会降低,具体分析参考OI-wiki Dinic算法特殊情境下的复杂度分析反正能过就对了

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int MX=1e6+7;
const ll INF=1e18+7;
struct Edge{
	int from,to;
	ll cap,flow;
	int nxt;
}edge[MX];
struct e{
	int u,v,c;
}cedge[MX];
bool cmp(e x,e y){return x.c<y.c;}
int head[MX],cur[MX],d[MX],tNode=0,cnt=0;
bool vis[MX];
int n,m,s,t,L;
void addE(int u,int v,ll c){
	edge[tNode].from=u;
	edge[tNode].to=v;
	edge[tNode].cap=c;
	edge[tNode].flow=0;
	edge[tNode].nxt=head[u];
	head[u]=tNode;
	tNode++;
}

queue<int > q; 

bool BFS() {
	memset(vis,false,sizeof(vis));
	d[s]=0;
	vis[s]=true;
	q.push(s);
	while(!q.empty()){
		int p=q.front();
		q.pop();
		for(int i=head[p];i!=-1;i=edge[i].nxt){
			int to=edge[i].to;
			if(!vis[to]&&edge[i].flow<edge[i].cap){
				d[to]=d[p]+1;
				vis[to]=true;
				q.push(to);
			}
		}
	}
	return vis[t];
}

ll DFS(int x,ll a){
	//cout<<x<<endl;
	if(x==t||a==0)return a;
	ll flow=0,f;
	for(int &i=cur[x];i!=-1;i=edge[i].nxt){
		int to=edge[i].to;
		if(d[x]+1==d[to]&&(f=DFS(to,min(a,edge[i].cap-edge[i].flow)))>0){
			flow+=f;
			a-=f;
			edge[i].flow+=f;
			edge[i^1].flow-=f;
			if(a==0)break;
		}
	}
	return flow;
}

ll DINIC(){
	ll ret=0;
	while(BFS()){
		for(int i=1;i<=n;i++)cur[i]=head[i];
		ret+=DFS(s,INF);
	}
	return ret;
}

void add(int x,int y,ll c){
	addE(x,y,c);
	addE(y,x,0);
}

ll solve(int l,int r){
	for(int i=1;i<=n;i++)head[i]=-1;
	tNode=0;
	for(int i=l;i<=r;i++){
		add(cedge[i].u,cedge[i].v,1);
		add(cedge[i].v,cedge[i].u,1);
	}
	return DINIC();
}

int main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++)cin>>cedge[i].u>>cedge[i].v>>cedge[i].c;
	sort(cedge+1,cedge+1+m,cmp);
	cin>>s>>t>>L;
	int pos1=0,pos2=0;
	for(int i=1;i<=m;i++){
		if(cedge[i].c>=L&&pos1==0)pos1=i;
		if(cedge[i].c>L&&pos2==0)pos2=i;
	}
	cout<<solve(1,pos1-1)+solve(pos2,m)<<endl;
	return 0;
}