题目
思路
狡猾的出题人把这句话悄悄写在了 输入格式
里面:
也就是,第 个人认识第 只猫。所以这两者只能选一个。可是我们又需要一共选出 个。所以 第 个人和第 只猫恰好有一个被选择。
然后就变成了 吧。不过约束条件只有一种: 则非 。所以我们可以优化过程,我们不建虚点,若 人认识 猫,则 向 连边,表示 选裁判则 选裁判。
对于这张图,我们仍然求解其强连通分量,因为一个强连通分量中的选择相同。如何构造一个解?
找到没有出度的一个强连通分量即可,因为它是无后效性的。代码中很好实现,就是 过程中的第一个。将该强连通分量全部设置为裁判,其余为选手。无解情况:只有一个强连通分量。
代码
#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;
}