题解:P4662 [BalticOI 2008] 黑手党

题目传送门

题目大意

从一个点 \(S\) 到另一个点 \(T\),截断指定点使 \(S\)\(T\) 不连通,并且费用最小,求截断哪些点。

思路

显然是最小割。

但是我们需要切割边而不是点,因此我们需要将指定点分成两个点,之间连一条流量为费用的边。所以对于一个指定的点 \(i\)
可以从 \(i\)\(i+n\) 连一条流量为 \(c_i\) 的边。此外对于一条路径 \((u,v)\),也就变成了 \((u+n,v)\)\((v+n,u)\) 两条边,又因为我们只能切割指定点被拆后出现的边,因此指定点之间的边的流量需要设成无穷大,使它不能被切割。

剩下的就是最小割模板了,这里 我用的是 Dinic。

割完之后,我们需要判断哪些指定点被割了。这是可以走一遍深搜,标记一下所有起点能到达的点,然后再遍历一遍指定点的拆点,如果一个被标记了另一个没有,则证明拆点之间的边被割了,即点被割了。而判断两点的布尔值是否相同自然是用异或了。

代码

#include<bits/stdc++.h>
#define int long long
#define MAX 200005
#define inf 0x7f7f7f7f //极大值 
using namespace std;
int n,m,S,T,c[MAX],cnt=1,head[MAX],maxflow;//cnt别忘了初值 
int now[MAX],dep[MAX];
bool vis[MAX];
struct MRS{
	int to,next,val;
}edge[MAX*2];
void add(int u,int v,int w){
	cnt++;
	edge[cnt].to=v;
	edge[cnt].val=w;
	edge[cnt].next=head[u];
	head[u]=cnt;
}
/*-------------------------Dinic模板-------------------------*/
bool bfs(){
	queue<int> q;
	memset(dep,0,sizeof(dep));
	memcpy(now,head,sizeof(now));
	dep[S]=1;
	q.push(S);
	while(!q.empty()){
		int u=q.front();
		q.pop();
		for(int i=head[u];i;i=edge[i].next){
			int v=edge[i].to,w=edge[i].val;
			if(!dep[v]&&w){
				q.push(v);
				dep[v]=dep[u]+1;
				if(v==T)return 1;
			}
		}
	}
	return 0;
}
int dfs(int u,int flow){
	if(u==T)return flow;
	int rest=flow,k=0;
	for(int i=now[u];i&&rest;i=edge[i].next){
		now[u]=i;
		int v=edge[i].to,w=edge[i].val;
		if(dep[v]==dep[u]+1&&w){
			k=dfs(v,min(rest,w));
			if(!k)dep[v]=0;
			edge[i].val-=k;
			edge[i^1].val+=k;
			rest-=k;
		}
	}
	return flow-rest;
}
/*-------------------------DFS遍历-------------------------*/
void DFS(int u){
	vis[u]=1;
	for(int i=head[u];i;i=edge[i].next){
		int v=edge[i].to,w=edge[i].val;
		if(!vis[v]&&w)vis[v]=1,DFS(v);
	}
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>m>>S>>T;
	T+=n;
	for(int i=1;i<=n;i++){
		cin>>c[i];
		add(i,i+n,c[i]);
		add(i+n,i,0);
	}
	for(int i=1,x,y;i<=m;i++){
		cin>>x>>y;
		add(x+n,y,inf);
		add(y,x+n,0);
		add(y+n,x,inf);
		add(x,y+n,0);
	}
	while(bfs())maxflow+=dfs(S,inf);
	DFS(S);
	for(int i=1;i<=n;i++)
		if(vis[i]^vis[i+n])cout<<i<<' ';//判断 
	return 0;
}
posted @ 2024-07-11 12:30  GyrthCurunír  阅读(2)  评论(0编辑  收藏  举报