将未建立贸易关系看成连一条边,那么这显然是个二分图。最大城市群即最大独立集,也即n-最大匹配。现在要求的就是删哪些边会使最大匹配减少,也即求哪些边一定在最大匹配中。
首先范围有点大,当然是跑个dinic,转化成最大流。会使最大流减少的边相当于可能在最小割中的边,因为删掉它就相当于无代价的割掉了一条边。那么用曾经看到过的结论就可以了:当且仅当该边满流且残余网络(包括反向边)中该边两端点处于不同SCC时,该边可能在最小割中。不太会证。于是tarjan一发就可以了。注意不要把开始给的图和网络流建图搞混。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; #define ll long long #define N 10010 #define M 300010 #define S 0 #define T 10001 char getc(){char c=getchar();while (c==10||c==13||c==32) c=getchar();return c;} int gcd(int n,int m){return m==0?n:gcd(m,n%m);} int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int n,m,p[N],t=-1,color[N],ans; int d[N],q[N],cur[N]; struct data{int to,nxt,cap,flow; }edge[M<<1]; struct data2 { int x,y; bool operator <(const data2&a) const { return x<a.x||x==a.x&&y<a.y; } }v[M]; void addedge(int x,int y,int z) { t++;edge[t].to=y,edge[t].nxt=p[x],edge[t].cap=z,edge[t].flow=0,p[x]=t; t++;edge[t].to=x,edge[t].nxt=p[y],edge[t].cap=0,edge[t].flow=0,p[y]=t; } void paint(int k) { for (int i=p[k];~i;i=edge[i].nxt) if (color[edge[i].to]==-1) { color[edge[i].to]=color[k]^1; paint(edge[i].to); } } bool bfs() { int head=0,tail=1;q[1]=S; memset(d,255,sizeof(d));d[S]=0; do { int x=q[++head]; for (int i=p[x];~i;i=edge[i].nxt) if (d[edge[i].to]==-1&&edge[i].flow<edge[i].cap) { d[edge[i].to]=d[x]+1; q[++tail]=edge[i].to; } }while (head<tail); return ~d[T]; } int work(int k,int f) { if (k==T) return f; int used=0; for (int i=cur[k];~i;i=edge[i].nxt) if (d[k]+1==d[edge[i].to]) { int w=work(edge[i].to,min(f-used,edge[i].cap-edge[i].flow)); edge[i].flow+=w,edge[i^1].flow-=w; if (edge[i].flow<edge[i].cap) cur[k]=i; used+=w;if (used==f) return f; } if (used==0) d[k]=-1; return used; } void dinic() { while (bfs()) { memcpy(cur,p,sizeof(p)); work(S,N); } } namespace newgraph { int dfn[N]={0},low[N]={0},stk[N],id[N],top=0,cnt=0,tot=0,t=0,p[N]={0},ans=0; bool flag[N]; struct data{int to,nxt;}edge[M]; void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;} void tarjan(int k) { dfn[k]=low[k]=++cnt; stk[++top]=k;flag[k]=1; for (int i=p[k];i;i=edge[i].nxt) if (!dfn[edge[i].to]) tarjan(edge[i].to),low[k]=min(low[k],low[edge[i].to]); else if (flag[edge[i].to]) low[k]=min(low[k],dfn[edge[i].to]); if (dfn[k]==low[k]) { tot++; while (stk[top]!=k) { flag[stk[top]]=0; id[stk[top]]=tot; top--; } flag[k]=0;id[k]=tot;top--; } } void work() { for (int i=0;i<=n;i++) if (!dfn[i]) tarjan(i); if (!dfn[T]) tarjan(T); } } int main() { #ifndef ONLINE_JUDGE freopen("a.in","r",stdin); freopen("a.out","w",stdout); const char LL[]="%I64d "; #else const char LL[]="%lld "; #endif n=read(),m=read(); memset(p,255,sizeof(p)); for (int i=1;i<=m;i++) { int x=read(),y=read(); addedge(x,y,1); } memset(color,255,sizeof(color)); for (int i=1;i<=n;i++) if (color[i]==-1) color[i]=1,paint(i); for (int i=0;i<=t;i++) edge[i].cap=color[edge[i^1].to]; for (int i=1;i<=n;i++) if (color[i]) addedge(S,i,1); else addedge(i,T,1); dinic(); for (int i=0;i<=t;i++) if (edge[i].flow<edge[i].cap) newgraph::addedge(edge[i^1].to,edge[i].to); newgraph::work(); for (int i=0;i<=t;i++) if (edge[i].cap==1&&edge[i].flow==edge[i].cap&&edge[i^1].to!=S&&edge[i].to!=T&&newgraph::id[edge[i^1].to]!=newgraph::id[edge[i].to]) ans++,v[ans].x=min(edge[i^1].to,edge[i].to),v[ans].y=max(edge[i^1].to,edge[i].to); sort(v+1,v+ans+1); cout<<ans<<endl; for (int i=1;i<=ans;i++) printf("%d %d ",v[i].x,v[i].y); return 0; }