题目
思路
狡猾的出题人把这句话悄悄写在了 输入格式
里面:
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.} It′sguaranteed,thatforeveryithereexistsapairbetweenithresidentandithcat.
也就是,第 i i i 个人认识第 i i i 只猫。所以这两者只能选一个。可是我们又需要一共选出 n n n 个。所以 第 i i i 个人和第 i i i 只猫恰好有一个被选择。
然后就变成了 2 - S A T \tt 2{\text -}SAT 2-SAT 吧。不过约束条件只有一种: a a a 则非 b b b 。所以我们可以优化过程,我们不建虚点,若 a t h a^{th} ath 人认识 b t h b^{th} bth 猫,则 a a a 向 b b b 连边,表示 a a a 选裁判则 b b b 选裁判。
对于这张图,我们仍然求解其强连通分量,因为一个强连通分量中的选择相同。如何构造一个解?
找到没有出度的一个强连通分量即可,因为它是无后效性的。代码中很好实现,就是 d f s \tt dfs 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;
}