题目大意
一副图告诉你那些点之间没有连边,并保证整幅图可以被划分为至多两个团。
请你找出每一对没有连边的点,使得它们连边后,图中最大团的大小会变大。
模型转化
因为原图要被分成至多两个团,其补图一定是二分图。
最大团大小等同于最大独立集大小,因此和最大匹配挂钩。
求哪些边一定在最大匹配上。
做法
先做最大匹配算法。
一定在最大匹配上的边一定是匹配边(显然)。
然后考虑三种情况会使得匹配边u->v不一定在最大匹配中。
1、在残余网络中,u可以到达T。
如果退流u->v,那么S就可以走u,又因为u能到达T,最大匹配不会变,则u->v不一定在最大匹配中。
2、在残余网络中,S可以到达v。
同理。
这两种情况随便搜一遍就可以判。
3、在残余网络中,u可以到达v。
如果退流u->v,那么S走到u,u走到v,v再到T,最大匹配不会变。
则u->v不一定在最大匹配中。
这种情况注意u->v是匹配边,所以v连了有向边到u,因此u和v处在一个环中。可以做一遍Tarjan,就能判掉。
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=10000+10,maxm=150000+10;
struct dong{
int x,y;
} ans[maxm];
int h2[maxn],g2[maxm*2],n2[maxm*2];
int h[maxn],go[maxm*2],next[maxm*2];
int h3[maxn],g3[maxm*2],n3[maxm*2];
int co[maxn],re[maxn],A[maxn],B[maxn],belong[maxn],dfn[maxn],low[maxn],sta[maxn];
int pd[maxn];
bool bz[maxn];
int i,j,k,l,r,s,t,n,m,tot,top,cnt,cnt1,cnt2;
int read(){
int x=0,f=1;
char ch=getchar();
while (ch<'0'||ch>'9'){
if (ch=='-') f=-1;
ch=getchar();
}
while (ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
void add(int x,int y){
go[++tot]=y;
next[tot]=h[x];
h[x]=tot;
}
void add2(int x,int y){
g2[++cnt]=y;
n2[cnt]=h2[x];
h2[x]=cnt;
}
void add3(int x,int y){
g3[++cnt]=y;
n3[cnt]=h3[x];
h3[x]=cnt;
}
void dfs(int x,int y){
co[x]=y;
if (y) B[++cnt2]=x;else A[++cnt1]=x;
int t=h2[x];
bz[x]=1;
while (t){
if (!bz[g2[t]]) dfs(g2[t],1-y);
t=n2[t];
}
}
int hungary(int x){
int t=h[x];
while (t){
if (pd[go[t]]<j){
pd[go[t]]=j;
if (re[go[t]]==0||hungary(re[go[t]])){
re[go[t]]=x;
return 1;
}
}
t=next[t];
}
return 0;
}
void dg(int x){
co[x]=1;
bz[x]=1;
int t=h[x];
while (t){
if (!bz[go[t]]&&re[go[t]]!=x){
bz[go[t]]=1;
co[go[t]]=1;
if (re[go[t]]&&!bz[re[go[t]]]) dg(re[go[t]]);
}
t=next[t];
}
}
void travel(int x,int y){
co[x]=2;
bz[x]=1;
if (y==1){
int t=h[x];
while (t){
if (re[x]!=go[t]&&!bz[go[t]]) travel(go[t],0);
t=next[t];
}
}
else{
int t=h[x];
while (t){
if (re[go[t]]==x&&!bz[go[t]]) travel(go[t],1);
t=next[t];
}
}
}
bool cmp(dong a,dong b){
return a.x<b.x||a.x==b.x&&a.y<b.y;
}
void tarjan(int x){
bz[x]=1;
sta[++top]=x;
pd[x]=1;
dfn[x]=low[x]=++cnt;
int t=h3[x];
while (t){
if (!bz[g3[t]]){
tarjan(g3[t]);
low[x]=min(low[x],low[g3[t]]);
}
else if (pd[g3[t]]) low[x]=min(low[x],dfn[g3[t]]);
t=n3[t];
}
if (dfn[x]==low[x]){
++tot;
do{
belong[sta[top]]=tot;
pd[sta[top]]=0;
top--;
}while (sta[top+1]!=x);
}
}
int main(){
freopen("a.in","r",stdin);freopen("a.out","w",stdout);
n=read();m=read();
fo(i,1,m){
j=read();k=read();
add2(j,k);add2(k,j);
}
fo(i,1,n)
if (!bz[i]) dfs(i,0);
fo(i,1,m){
j=g2[i*2-1];k=g2[i*2];
if (co[j]) swap(j,k);
add(j,k);add(k,j);
}
fo(i,1,cnt1){
j++;
hungary(A[i]);
}
fo(i,1,n) co[i]=bz[i]=pd[i]=0;
fo(i,1,cnt2){
k=B[i];
if (re[k]) pd[re[k]]=1;
}
fo(i,1,cnt1){
k=A[i];
if (!bz[k]&&!pd[k]) dg(k);
}
fo(i,1,n) bz[i]=0;
fo(i,1,cnt2){
k=B[i];
if (!bz[k]&&re[k]==0) travel(k,1);
}
cnt=0;
fo(i,1,cnt1){
k=A[i];
t=h[k];
while (t){
if (re[go[t]]==k) add3(go[t],k);else add3(k,go[t]);
t=next[t];
}
}
cnt=top=tot=0;
fo(i,1,n) bz[i]=pd[i]=0;
fo(i,1,n)
if (!bz[i]) tarjan(i);
top=0;
fo(i,1,m){
j=go[i*2];k=go[i*2-1];
if (re[k]==j&&co[j]!=2&&co[k]!=1&&belong[j]!=belong[k]){
if (j>k) swap(j,k);
ans[++top].x=j;
ans[top].y=k;
}
}
sort(ans+1,ans+top+1,cmp);
printf("%d\n",top);
fo(i,1,top) printf("%d %d\n",ans[i].x,ans[i].y);
}