有 (n) 个人和 (n) 只猫。有 (m) 对人猫友谊,即第 (u_i) 个人认识第 (v_i) 只猫,保证第 (i) 个人和第 (i) 只猫认识。求 (j) 个人和 (p) 只猫使 (j+p=n(1le j,p<n)),并且这些人和猫互不认识。(t) 组测试数据。
数据范围: (1le nle mle 10^6),(1le sum n,sum mle 10^6)。
首先这是个二分图,人一块,猫一块。
将第 (i) 个人和第 (i) 只猫合并成一点,因为他们不能都选。
从 (u_i) 向 (v_i) 连一条有向边,表示如果第 (u_i) 点选人,(v_i) 点也必须选人。
如果出现环,则这些点要么全选人要么全选猫,不妨令选人。
所以 ( exttt{Tarjan}) 把强联通分量分出来。
如果只有一个强联通分量,则必须全选人,与 ((1le j,p<n)) 不符,输出 ( exttt{No})。
否则选出拓扑序最后的强联通分量全选人,其他全选猫。
( exttt{Tarjan}) 有个性质:编号为 (1) 的强联通分量拓扑序最后。
时间复杂度 (Theta(n+m)),空间复杂度 (Theta(n+m))。
- 代码:
#include <bits/stdc++.h>
using namespace std;
//Start
typedef long long ll;
typedef double db;
#define mp(a,b) make_pair(a,b)
#define x(a) a.first
#define y(a) a.second
#define b(a) a.begin()
#define e(a) a.end()
#define sz(a) int((a).size())
#define pb(a) push_back(a)
const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f;
//Data
const int N=1e6;
int n,m;
vector<int> e[N+7];
//Tarjan
int ic,in[N+7],low[N+7],dfn[N+7],sc,st[N+7],cc,co[N+7],sm[N+7];
void Tarjan(int u){
low[u]=dfn[u]=++ic,in[u]=1,st[++sc]=u;
for(int v:e[u])
if(!dfn[v]) Tarjan(v),low[u]=min(low[u],low[v]);
else if(in[v]) low[u]=min(low[u],dfn[v]);
if(dfn[u]==low[u]) for(int v=0,t=++cc;v!=u;) v=st[sc--],co[v]=t,in[v]=0,sm[t]++;
}
//KonnyWen
void KonnyWen(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) e[i].clear();
for(int i=1;i<=m;i++){
int u,v; scanf("%d%d",&u,&v);
if(u!=v) e[u].pb(v);
}
fill(dfn+1,dfn+n+1,0),fill(sm+1,sm+n+1,0),ic=sc=cc=0;
for(int i=1;i<=n;i++)if(!dfn[i]) Tarjan(i);
if(cc==1) return void(puts("No"));
printf("Yes
%d %d
",sm[1],n-sm[1]);
for(int i=1;i<=n;i++)if(co[i]==1) printf("%d ",i); puts("");
for(int i=1;i<=n;i++)if(co[i]!=1) printf("%d ",i); puts("");
}
//Main
int main(){
int t; scanf("%d",&t);
for(int ti=1;ti<=t;ti++) KonnyWen();
return 0;
}
祝大家学习愉快!