题意:
现在有n个人,n只猫,给你他们的联系,并且第i个人与第i只猫一定有联系。现在需要选出总共n个人+猫,而且至少有一个人,一只猫,问你选的人是哪些,猫是哪些
题解:
第一次接触tarjan,就是求强联通分量的一种方法。
scc相同即在同一个强联通分量里。
那么这道题为什么会扯上scc,是因为我们看到需要n个人+猫,而第i个人与第i只猫一定有联系,说明第i个人与第i只猫中一定要选一个。而如果这张二分图是一个强联通分量的话,由于我们至少需要一个人,但是无论我们取哪个人,都会否决掉至少两只猫,那么又会牵扯上一定要选另一个人,然后又会否决更多的猫,到最后一定是选所有的人。但是我们又至少需要一只猫,所以无法满足条件。
如果有解,那么只要将scc为1的人选出来,剩下的就是猫即可。
选scc为1的是因为scc为1的强联通缩点之后没有出边。
#include<bits/stdc++.h>
using namespace std;
const int N=2e6+5;
struct node{
int to,next;
}e[N*2];
int cnt,head[N];
void add(int x,int y){
e[cnt].to=y;
e[cnt].next=head[x];
head[x]=cnt++;
}
// scc
int low[N],dfn[N],scc[N],tim,num;
stack<int>st;
bool in[N];
// init
void init(int siz) {
memset(head,-1,sizeof(int)*(siz+1));
cnt=tim=num=0;
memset(dfn,0,sizeof(int)*(siz+1));
memset(in,0,sizeof(bool)*(siz+1));
while(!st.empty())
st.pop();
}
// deal
void tarjan(int x) {
low[x]=dfn[x]=++tim;
st.push(x);
in[x]=1;
for(int i=head[x];~i;i=e[i].next) {
int ne=e[i].to;
if(!dfn[ne])
tarjan(ne),low[x]=min(low[x],low[ne]);
else if(in[ne])
low[x]=min(low[x],dfn[ne]);
}
if(low[x]==dfn[x]) {
++num;
while(1) {
int v=st.top();
st.pop();
scc[v]=num;
in[v]=0;
if(x==v)
break;
}
}
}
vector<int>a1,a2;
int main()
{
int t;
scanf("%d",&t);
while(t--){
int n,m;
scanf("%d%d",&n,&m);
init(n*2);
int x,y;
for(int i=1;i<=m;i++)
scanf("%d%d",&x,&y),add(x,n+y);
for(int i=1;i<=n;i++)
add(n+i,i);
for(int i=1;i<=n;i++)
if(!dfn[i])
tarjan(i);
if(num==1){
printf("No\n");
continue;
}
a1.clear(),a2.clear();
for(int i=1;i<=n;i++)
if(scc[i]==1)
a1.push_back(i);
else
a2.push_back(i);
printf("Yes\n");
printf("%d %d\n",a1.size(),a2.size());
for(auto i:a1)
printf("%d ",i);
printf("\n");
for(auto i:a2)
printf("%d ",i);
printf("\n");
}
return 0;
}
/*
1
3 6
1 1
1 2
2 1
2 2
2 3
3 3
*/