Problem
Luogu
不知道为什么好像没有在bzoj上找到这道题?
Solution
首先你要看出来,未联通的城市的最大独立集就是最大城市群。因为城市群是任意两两之间都有连边的,那么也就意味着在给出的图中,任意两两都没有连边,这就是独立集。
那么问题就变成了,删掉哪些边可以使得最大独立集变大1,即最大流减小1。有一个这样的定理,关键边在最大流中必定满流且连接的两点在残量网络中并不在一个强连通分量中。可以思考反证,如果在一个强联通分量中,那么就存在一条可以替代的边。
我也不知道这么大的数据为什么网络流没有TLE,dalao告诉我Dinic在二分图中跑得相当快。由于我懒得建两个图,而染色和修改权值的部分我调了很久,所以代码有点冗长。
Code
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <queue>
#define rg register
using namespace std;
typedef long long ll;
const int maxn=10010,maxm=150010,INF=0x3f3f3f3f;
template <typename Tp> inline void getmin(Tp &x,Tp y){if(y<x) x=y;}
template <typename Tp> inline void getmax(Tp &x,Tp y){if(y>x) x=y;}
template <typename Tp> inline void read(Tp &x)
{
x=0;int f=0;char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') f=1,ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
if(f) x=-x;
}
struct Edge{
int u,v;
bool operator < (const Edge &t)const{return u==t.u?v<t.v:u<t.u;}
}a[maxm];
struct data{int v,w,nxt;}edge[maxm<<1];
int n,m,p=-1,tot,s=10001,t=10002,head[maxn],dis[maxn],cor[maxn],ful[maxn];
int dfc,top,idc,dfn[maxn],low[maxn],vis[maxn],stk[maxn],id[maxn];
queue<int> q;
inline void insert(int u,int v,int w)
{
edge[++p]=(data){v,w,head[u]};head[u]=p;
edge[++p]=(data){u,0,head[v]};head[v]=p;
}
void color(int x)
{
for(int i=head[x];~i;i=edge[i].nxt)
if(cor[edge[i].v]==-1)
cor[edge[i].v]=cor[x]^1,color(edge[i].v);
}
void input()
{
read(n);read(m);
if(!m){puts("0");exit(0);}
memset(cor,0xff,sizeof(cor));
memset(head,0xff,sizeof(head));
for(rg int i=1;i<=m;i++)
{read(a[i].u);read(a[i].v);if(a[i].u>a[i].v) swap(a[i].u,a[i].v);insert(a[i].u,a[i].v,0);}
cor[s]=0;cor[t]=1;
for(rg int i=1;i<=n;i++)
if(cor[i]==-1)
cor[i]=0,color(i);
for(rg int i=0;i<=p;i++)
if(cor[edge[i].v]) edge[i].w=1;
for(rg int i=1;i<=n;i++)
cor[i]?insert(i,t,1):insert(s,i,1);
}
int bfs(int s,int t)
{
int x;
memset(dis,0xff,sizeof(dis));
q.push(s);dis[s]=0;
while(!q.empty())
{
x=q.front();q.pop();
for(int i=head[x];~i;i=edge[i].nxt)
if(edge[i].w&&dis[edge[i].v]==-1)
{
dis[edge[i].v]=dis[x]+1;
q.push(edge[i].v);
}
}
return ~dis[t];
}
int dfs(int x,int t,int flow)
{
if(x==t||!flow) return flow;
int rest=0,tmp;
for(int i=head[x];~i;i=edge[i].nxt)
if(dis[edge[i].v]==dis[x]+1)
{
tmp=dfs(edge[i].v,t,min(edge[i].w,flow-rest));
edge[i].w-=tmp;edge[i^1].w+=tmp;rest+=tmp;
if(rest==flow) break;
}
if(!rest) dis[x]=-1;
return rest;
}
void tarjan(int x)
{
dfn[x]=low[x]=++dfc;stk[++top]=x;vis[x]=1;
for(int i=head[x];~i;i=edge[i].nxt)
if(edge[i].w)
{
if(!dfn[edge[i].v]) tarjan(edge[i].v),getmin(low[x],low[edge[i].v]);
if(vis[edge[i].v]) getmin(low[x],dfn[edge[i].v]);
}
if(dfn[x]==low[x])
{
int tmp;++idc;
do{
tmp=stk[top--];
id[tmp]=idc;vis[tmp]=0;
}while(tmp^x);
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
int fw=0,cnt;
input();
while(bfs(s,t))
while(cnt=dfs(s,t,INF))
fw+=cnt;
tarjan(s);
for(rg int i=1;i<=n;i++)
if(!dfn[i])
tarjan(i);
sort(a+1,a+m+1);
for(rg int i=1;i<=m;i++)
{
if(a[i].u^a[i-1].u)
{
memset(ful,0,sizeof(ful));
for(int j=head[a[i].u];~j;j=edge[j].nxt)
if(cor[edge[j].v]^cor[a[i].u])
ful[edge[j].v]=(cor[a[i].u]?edge[j].w:edge[j^1].w);
}
if(id[a[i].u]!=id[a[i].v]&&ful[a[i].v])
a[++tot]=a[i];
}
printf("%d\n",tot);
for(rg int i=1;i<=tot;i++) printf("%d %d\n",a[i].u,a[i].v);
return 0;
}