题面传送门
我们将每个点拆成入点和出点,然后每条边入点和出点连边,表示这两个点可以在一条路径上。
那么总的答案就是点数减去匹配数。
输出方案这个东西也很好搞,就是对于每个点找到答案,然后直接并查集维护即可。
code:
#include<bits/stdc++.h> #define I inline #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) #define abs(x) ((x)>0?(x):-(x)) #define re register #define ll long long #define db double #define N 300 #define M 20000 #define mod 31011 #define eps (1e-7) #define U unsigned int #define it iterator #define Gc() getchar() #define Me(x,y) memset(x,y,sizeof(x)) using namespace std; int n,m,S,T,d[N+5],nows[N+5],x,y,now,fa[N+5],un,wn,ans; struct yyy{int to,w,z;}tmp; struct ljb{int head,h[N+5];yyy f[M+5<<1];I void add(int x,int y,int z){f[head]=(yyy){y,z,h[x]};h[x]=head++;}}s; I void get(int x,int y,int z){s.add(x,y,z);s.add(y,x,0);} queue<int> Q; I int bfs(){ while(!Q.empty()) Q.pop();yyy tmp;Me(d,0x3f);d[S]=0;Q.push(S);nows[S]=s.h[S];while(!Q.empty()){ now=Q.front();Q.pop();for(int i=s.h[now];~i;i=tmp.z){ tmp=s.f[i];if(!tmp.w||d[tmp.to]<1e9) continue;d[tmp.to]=d[now]+1; nows[tmp.to]=s.h[tmp.to];Q.push(tmp.to);if(tmp.to==T)return 1; } }return 0; } I int dfs(int x,int sum){ if(x==T) return sum;yyy tmp;int i,k,pus=0;for(i=nows[x];~i;i=tmp.z){ nows[x]=i;tmp=s.f[i];if(!tmp.w||d[tmp.to]!=d[x]+1) continue;k=dfs(tmp.to,min(sum,tmp.w)); if(!k) d[tmp.to]=1e9;sum-=k;pus+=k;s.f[i].w-=k;s.f[i^1].w+=k;if(!sum) break; }return pus; }vector<int> Ans[N+5]; I int Getfa(int x){return x==fa[x]?x:fa[x]=Getfa(fa[x]);} I void merge(int x,int y){un=Getfa(x);wn=Getfa(y);un^wn&&(fa[un]=wn);} int main(){ freopen("1.in","r",stdin); re int i,j;Me(s.h,-1);scanf("%d%d",&n,&m);T=2*n+1;for(i=1;i<=m;i++) scanf("%d%d",&x,&y),get(x,y+n,1);for(i=1;i<=n;i++)get(S,i,1),get(i+n,T,1),fa[i]=i; while(bfs()) ans+=dfs(S,1e8);for(i=1;i<=n;i++) { for(j=s.h[i];~j;j=tmp.z)tmp=s.f[j],!tmp.w&&tmp.to&&(merge(i,tmp.to-n),0); }for(i=1;i<=n;i++) Ans[Getfa(i)].push_back(i); for(i=1;i<=n;i++) {if(!Ans[i].size()) continue;for(j=0;j<Ans[i].size();j++) printf("%d ",Ans[i][j]);printf("\n");} printf("%d\n",n-ans); }