其他分享
首页 > 其他分享> > P5782 [POI2001]和平委员会

P5782 [POI2001]和平委员会

作者:互联网

基于并查集的缩点 \(Dfs\)

标题党,jbl

这个裸的 \(2-Sat\) 就不用说了吧,这里只讲思路

读完题目,定义 \(x\) 与 \(x^{'}\) 一组,我们对于每一条规定转化一下,其实就是:

\(a\) 必须与 \(b^{'}\) 一组,\(b\) 必须与\(a^{'}\)一组,\(rule_1\)

依照 \(rule_1\) 进行建图,思考建完图后,我们如何得到正解。

首先一个显而易见的是:如果同一组的 \(2\) 个点都在同一强连通分量内,那么肯定无解,因为一旦选了强连通分量内的一点,那么整个强连通分量的点都要被选,这与题目中每个党派都在委员会中恰有 \(1\) 个代表矛盾,\(rule_2\)。

再回过头来,发现缩完点后我们的图是个 \(DAG\),想到拓扑排序。

如果对于同一组的两个点 \(x\) 与 \(x^{'}\),我们定义 \(rule_3\):优先选择拓扑序后的结点。为什么呢?

其实原因很简单,因为这是 \(DAG\),而拓扑排序越早记录到的结点,就越容易影响到后面的结点,简单来说,就是有后效性。

最后再来讲一下标题。因为题解区好像都是tarjan?

其实就是暴力\(Dfs\)啦。

记 \(dep[i]\) 为 \(Dfs\) 中搜索到的序号,那么有以下代码:

void dfs(int pos){
	for(int i=h[pos]; i; i=edge[i].nxt){
		int to=edge[i].to;
		if(!dep[to]){\\1
			dep[to]=dep[pos]+1;
			dfs(to);
		}
		if(dep[find(to)]>0){\\2
			if(dep[find(pos)]<dep[f[to]]) 
				f[f[to]]=f[pos];
			else f[f[pos]]=f[to];
		}
	}
	dep[pos]=-1, top_mark[pos]=++cnt;\\3
}

一步一步讲:

  1. 没走到的点,走下去。

  2. 如果我所到的点,仍在递归栈中(就是走出了一个环),那么将这个点的集合并入并查集中。

  3. 修改 \(dep\),使得下一次搜索无法搜到。


Code:

#include <stdio.h>
#include <algorithm>
#include <string.h>
#define N 80000
#define M 20000
using namespace std;
int f[N*2+5];
int find(int x){
	if(f[x]==x) return x;
	return f[x]=find(f[x]);
}
struct Edge{
	int to, nxt;
}edge[2*M+5];
int tot, h[N+5];
void add(int a, int b){
	edge[++tot].to=b, edge[tot].nxt=h[a];
	h[a]=tot;
}
int n, m, cnt;
int dep[N+5], top_mark[N+5];
void dfs(int pos){
	for(int i=h[pos]; i; i=edge[i].nxt){
		int to=edge[i].to;
		if(!dep[to]){
			dep[to]=dep[pos]+1;
			dfs(to);
		}
		if(dep[find(to)]>0){
			if(dep[find(pos)]<dep[f[to]]) 
				f[f[to]]=f[pos];
			else f[f[pos]]=f[to];
		}
	}
	dep[pos]=-1, top_mark[pos]=++cnt;//记拓扑序 
}
int main(){
	int a, b;
	scanf("%d%d", &n, &m);
	for(int i=1; i<=2*n; i++) f[i]=i;
	for(int i=1; i<=m; i++){
		scanf("%d%d", &a, &b);
		int a_=a%2?a+1:a-1, b_=b%2?b+1:b-1;
		add(a, b_), add(b, a_);
	}	
	for(int i=1; i<=n*2; i++)
		if(!dep[i]) dfs(dep[i]=i);
	for(int i=1; i<=n*2; i+=2)
		if(find(i)==find(i+1)) return printf("NIE")*0;
	for(int i=1; i<=n*2; i+=2){
		int i_=i+1;
		if(top_mark[find(i)]<top_mark[find(i_)])
			printf("%d\n", i);
		else printf("%d\n", i_);
	}
	return 0;
}

\(over\)

标签:P5782,int,POI2001,pos,tot,dep,edge,和平,find
来源: https://www.cnblogs.com/sizeof127/p/14614505.html