[CF1239D]Catowice City

题目

传送门 to luogu

思路

狡猾的出题人把这句话悄悄写在了 输入格式 里面:

I t s    g u a r a n t e e d ,    t h a t    f o r    e v e r y    i    t h e r e    e x i s t s    a    p a i r    b e t w e e n    i t h    r e s i d e n t    a n d    i t h    c a t . {\tt It's\; guaranteed,\; that\; for\; every\;} i\; {\tt there\; exists\; a\; pair\; between\;} i^{th}\; {\tt resident\; and\;} i^{th}\; {\tt cat.}

也就是,第 i i 个人认识第 i i 只猫。所以这两者只能选一个。可是我们又需要一共选出 n n 个。所以 i i 个人和第 i i 只猫恰好有一个被选择

然后就变成了 2 - S A T \tt 2{\text -}SAT 吧。不过约束条件只有一种: a a 则非 b b 。所以我们可以优化过程,我们不建虚点,若 a t h a^{th} 人认识 b t h b^{th} 猫,则 a a b b 连边,表示 a a 选裁判则 b b 选裁判。

对于这张图,我们仍然求解其强连通分量,因为一个强连通分量中的选择相同。如何构造一个解?

找到没有出度的一个强连通分量即可,因为它是无后效性的。代码中很好实现,就是 d f s \tt dfs 过程中的第一个。将该强连通分量全部设置为裁判,其余为选手。无解情况:只有一个强连通分量。

代码

#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
typedef long long int_;
inline int readint(){
	int a = 0; char c = getchar(), f = 1;
	for(; c<'0'||c>'9'; c=getchar())
		if(c == '-') f = -f;
	for(; '0'<=c&&c<='9'; c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}
template < typename T >
void getMin(T&a,const T&b){ if(b<a)a=b; }

const int MaxN = 1000005;
int n, m;

struct Edge{
	int to, nxt;
	Edge(){ }
	Edge(int T,int N){
		to = T, nxt = N;
	}
};
Edge e[MaxN<<1];
int head[MaxN], cntEdge;
void clear(){
	for(int i=1; i<=n; ++i)
		head[i] = -1;
	cntEdge = 0;
}
/** @brief add a directed edge */
void addEdge(int a,int b){
	e[cntEdge] = Edge(b,head[a]);
	head[a] = cntEdge ++;
}

bool inSta[MaxN];
vector< int > sta;
int bel[MaxN];
int dfn[MaxN], low[MaxN], dfsClock;
int lastOne; // 没有出度的一个
void tarjan(int x){
	dfn[x] = low[x] = ++ dfsClock;
	inSta[x] = 1, sta.push_back(x);
	for(int i=head[x]; ~i; i=e[i].nxt){
		if(!dfn[e[i].to]){
			tarjan(e[i].to);
			getMin(low[x],low[e[i].to]);
		}
		else if(inSta[e[i].to])
			getMin(low[x],dfn[e[i].to]);
	}
	if(low[x] == dfn[x]){
		int y; do{
			y = sta.back(), sta.pop_back();
			bel[y] = x, inSta[y] = 0;
		}while(y != x);
		if(!lastOne) lastOne = x;
	}
}

int main(){
	for(int T=readint(); T; --T){
		n = readint(), m = readint();
		clear();
		for(int i=1; i<=m; ++i){
			int a = readint();
			addEdge(a,readint());
		}
		for(int i=1; i<=n; ++i)
			dfn[i] = 0; // 未访问
		lastOne = dfsClock = 0;
		for(int i=1; i<=n; ++i)
			if(!dfn[i])
				tarjan(i);
		int calc = 0; // scc个数
		int ren = 0; // 裁判数量
		for(int i=1; i<=n; ++i){
			if(bel[i] == i)
				++ calc;
			if(bel[i] == lastOne)
				++ ren;
		}
		if(calc == 1){
			puts("NO"); continue;
		}
		puts("YES");
		printf("%d %d\n",ren,n-ren);
		for(int i=1; i<=n; ++i)
			if(bel[i] == lastOne)
				printf("%d ",i);
		putchar('\n');
		for(int i=1; i<=n; ++i)
			if(bel[i] != lastOne)
				printf("%d ",i);
		putchar('\n');
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42101694/article/details/108127373