专栏文章

题解:P4662 [BalticOI 2008] 黑手党 (Day0)

P4662题解参与者 2已保存评论 1

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
1 条
当前快照
1 份
快照标识符
@minjppp1
此快照首次捕获于
2025/12/02 03:32
3 个月前
此快照最后确认于
2025/12/02 03:32
3 个月前
查看原文

思路:

很明显,这是一道最小割的题。
但是但是,难点在于建边。
我们将一个点 xx 拆开为入点 xx 和出点 x+nx+n。即把一条边 (x,y)(x,y) 拆为 (x+n,y)(x+n,y)(y+n,x)(y+n,x)。而我们只能割某个点被拆后出现的边,所以某个点之间的边的流量要设置成 INF,使它无法被割。
然后剩下的就是最小割模板啦,跑 Dinic 就好啦。
割完之后,再跑一遍 DFS 判断哪些点被割了即可。

CodeCPP
#include<bits/stdc++.h>
#define int long long
using namespace std ;
const int maxn=1e3+5;
const int maxm=1e5+5;
const int INF=0x3f3f3f3f;
struct node {
	int v;
	int id;
};
int n,m;
int ss,tt;
int ans=0;
int pre[maxn],pre1[maxn];
int dep[maxn];
int a[maxn];
vector<node>g[maxn];
int tot=0;
int w[maxm];
bool vis[maxn];
void add (int u,int v,int ww) {
	g[u].push_back({v,tot});
	g[v].push_back({u,tot^1});
	w[tot]=ww;
	tot+=2;
}
bool bfs (int x) {
	memset(dep,0,sizeof(dep));
	memset(pre,0,sizeof(pre));
	memset(pre1,0,sizeof(pre1));
	queue<int>q;
	q.push(x);
	dep[x]=1;
	while (!q.empty()) {
		int u=q.front();
		q.pop();
		for (auto i:g[u]) {
			int v=i.v;
			int id=i.id;
			if (dep[v]==0&&w[id]>0) {
				dep[v]=dep[u]+1;
				q.push(v);
			}
		}
	}
	return dep[tt];
}
int dfs (int u,int f) {
	if (u==tt) return f;
	int res=0;
	for (auto i:g[u]) {
		int v=i.v;
		int id=i.id;
		if (dep[v]<=dep[u]) continue;
		if (w[id]==0) continue;
		int f2=dfs(v,min(f-res,w[id]));
		res+=f2;
		w[id]-=f2;
		w[id^1]+=f2;
		if (res==f) break;
	}
	return res;
}
void dinic () {
	while (bfs(ss)) ans+=dfs(ss,INF);
}
void getg (int u) {
	vis[u]=1;
	for (auto i:g[u]) {
		int v=i.v;
		int id=i.id;
		if (!vis[v]&&w[id]>0) getg(v);
	}
}

signed main () {
	cin>>n>>m>>ss>>tt; tt+=n;
	for (int i=1;i<=n;i++) cin>>a[i];
	for (int i=1;i<=m;i++) {
		int x,y;
		cin>>x>>y;
		add(x+n,y,INF);
		add(y+n,x,INF);
	}
	for (int i=1;i<=n;i++) {
		if (i!=tt) add(i,i+n,a[i]);
	}
	dinic();
	getg(ss);
	for (int i=1;i<=n;i++) if (vis[i]^vis[i+n]) cout<<i<<' ';
	return 0;
}

评论

1 条评论,欢迎与作者交流。

正在加载评论...