这题好像是DEF里最水的,,
/* 建图:如果a认识b,那么从a->b连一条边,将点分成两个集合A,B,没有从A->B的边 求出强连通分量,再造一张新图,新图中任取一个的出度为0的点作为集合A即可 */ #include<bits/stdc++.h> using namespace std; #define N 2000005 struct Edge{int to,nxt;}e[N<<1]; int head[N],tot,n,m; int c[N],out[N],cnt;//新图的点个数,每个点的出度 int ind,dfn[N],low[N],stk[N],top,ins[N]; void tarjan(int x){ dfn[x]=low[x]=++ind; stk[++top]=x;ins[x]=1; for(int i=head[x];i!=-1;i=e[i].nxt){ int y=e[i].to; if(!dfn[y]){ tarjan(y); low[x]=min(low[x],low[y]); } else if(ins[y]) low[x]=min(low[x],dfn[y]); } if(dfn[x]==low[x]){ cnt++; int y; do{ y=stk[top--]; ins[y]=0; c[y]=cnt; }while(x!=y); } } void init(){ cnt=tot=ind=0; for(int i=0;i<=2*n;i++) low[i]=dfn[i]=ins[i]=out[i]=c[i]=0,head[i]=-1; } void add(int u,int v){ e[tot].to=v;e[tot].nxt=head[u];head[u]=tot++; } int main(){ int t;cin>>t;while(t--){ cin>>n>>m; init(); for(int i=1;i<=m;i++){ int u,v;scanf("%d%d",&u,&v); if(u!=v)add(u,v); } for(int i=1;i<=n;i++) if(!dfn[i])tarjan(i); for(int u=1;u<=n;u++) for(int i=head[u];i!=-1;i=e[i].nxt){ int v=e[i].to; if(c[u]!=c[v]) out[c[u]]++; } int ans=0; for(int i=1;i<=cnt;i++) if(out[i]==0){ans=i;break;} if(ans==0||cnt==1){puts("No");continue;} puts("Yes"); int cnta=0,cntb=0; for(int i=1;i<=n;i++) if(c[i]==ans)cnta++; else cntb++; cout<<cnta<<" "<<cntb<<' '; for(int i=1;i<=n;i++)if(c[i]==ans)cout<<i<<" ";puts(""); for(int i=1;i<=n;i++)if(c[i]!=ans)cout<<i<<" ";puts(""); } } /* 9 21 1 7 5 7 4 8 1 1 4 4 7 3 3 3 6 3 6 6 5 5 7 7 8 2 9 2 3 1 8 8 9 9 2 2 1 5 6 7 2 6 6 4 */