[ARC143D] Bridges 题解

发布时间 2023-10-10 16:26:39作者: xuantianhao

[ARC143D] Bridges

题意:给定 \(2n\) 个点和 \((u_1,v_1) , \cdots , (u_m,v_m)\),选择让 \(u_i\)\(v_i+n\)\(v_i\)\(u_i+n\),以最小化图中桥的个数。

有种技巧叫拆点,把一个点拆成入点和出点,看这个形式非常像拆点,于是先想想合并。

若有 \(n\) 个点,连上所有 \(u_i\)\(v_i\),是桥的边在原图中只能是桥(路径唯一),抛出结论:可以通过构造,让其他边均不是桥。

如果现在在看一个连通块,只需要通过把边重定向以使得这一块强连通,即可。

考虑去掉那些桥后一个一个连通块处理:顺着 DFS 树走下去,遇到返祖边向上定向,否则向下定向,即得。

以前听说过拆点这个想法,但这是第一次见到,甚至连可以合并两个点都没想到,只能说是发散思维的极大不足了。确实想到了某种 \(i\)\(i+n\) 间奇怪的对应关系,但“桥”导致往 tarjan 上想,这是复杂化了问题。

代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=5e6+100;
struct node{
	int to,next;
}e[N];
int n,m,cnt=1;
int a[N],b[N],head[N],dir[N];
bool vis[N],vis1[N];
void add(int x,int y){
	e[++cnt]={y,head[x]};
	head[x]=cnt;
}
void dfs(int now,int fa){
	if(vis1[now]) return ;
	vis1[now]=1;
	for(int i=head[now];i;i=e[i].next){
		int v=e[i].to;
		if((i^1)==fa||vis[i]) continue;
		vis[i^1]=vis[i]=1;
		dir[i]=1;
		dir[i^1]=0;
		dfs(v,i);
	}
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=m;i++) cin>>a[i];
	for(int i=1;i<=m;i++) cin>>b[i];
	for(int i=1;i<=m;i++) add(a[i],b[i]),add(b[i],a[i]);
	for(int i=1;i<=n;i++) if(!vis1[i]) dfs(i,0);
	for(int i=1;i<=m;i++) cout<<dir[i<<1];
	return 0;
}