每个点至多被一条路径覆盖,考虑网络流中约束是边
每个点拆成两个点 \((x, x')\) ,可以向自己连向的点做匹配 \((x' \to y)\),考虑一开始是 \(n\) 条路径,每匹配一个点就减少一条路径
答案就是 \(n\) - 最大匹配数
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<vector>
#include<cctype>
#include<cstring>
using namespace std;
#define rg register
inline int read(){
rg char ch=getchar();
rg int x=0,f=0;
while(!isdigit(ch)) f|=(ch=='-'),ch=getchar();
while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return f?-x:x;
}
int n,m;
const int N=2005,M=6005<<2;
int head[N],ver[M],nxt[M],flow[M],tot=1;
int dis[N],cur[N];
inline void add(int x,int y,int z){
ver[++tot]=y;
flow[tot]=z;
nxt[tot]=head[x];
head[x]=tot;
}
inline void adds(int x,int y,int z){
add(x,y,z);
add(y,x,0);
}
int s,t,vis[N];
inline int bfs(){
queue<int> q;
q.push(s);
memset(dis,0,sizeof dis);
dis[s]=1;
while(!q.empty()){
int x=q.front();q.pop();
cur[x]=head[x];
for(int y,i=head[x];i;i=nxt[i]){
y=ver[i];
if(!dis[y]&&flow[i]){
dis[y]=dis[x]+1;
q.push(y);
}
}
}
return dis[t];
}
int dfs(int x,int f){
if(x==t) return f;
int used=0;
for(int y,w,&i=cur[x];i;i=nxt[i]){
y=ver[i];
if(dis[y]==dis[x]+1&&flow[i]){
w=dfs(y,min(f-used,flow[i]));
if(w){
flow[i]-=w;
flow[i^1]+=w;
used+=w;
if(used==f) return f;
}
}
}
if(!used) dis[x]=0;
return used;
}
inline int dinic(){
int ans=0;
while(bfs()) ans+=dfs(s,0x3f3f3f3f);
return ans;
}
void work(int x){
printf("%d ",x);
vis[x]=true;
for(int i=head[x];i;i=nxt[i]){
if(i&1||flow[i]) continue;
work(ver[i]-n);
}
}
signed main(){
n=read(),m=read();
s=0;t=n<<1|1;
for(int x,y,i=1;i<=m;++i){
x=read(),y=read();
adds(x,y+n,1);
}
for(int i=1;i<=n;++i) adds(s,i,1),adds(i+n,t,1);
int ans=n-dinic();
for(int i=1;i<=n;++i) if(!vis[i]) work(i),puts("");
printf("%d\n",ans);
return 0;
}