题解
因为度数最少为\(k\),所以与\(i\)连接且不选的边最多为\(deg_i-k\)条。将\(u\)集合中的点与源点、将\(v\)集合中的点与汇点连接容量为\(deg_i-k\)(\(deg_i\)为\(i\)号节点的度数)的边,保留原图中的边,容量为\(1\)。该图最大流未经过的边即为选择的边。不过暴力枚举\(k\)时间复杂度为\(O(nm^2)\),无法通过。因此需要从\(minDegree\)到\(1\)倒叙枚举\(k\),每次将与源点、汇点相连的边容量\(+1\),在之前的剩余网络中跑最大流即可。时间复杂度约为\(O(nm)\)。
AC代码
#include<bits/stdc++.h>
#define pb push_back
using namespace std;
const int N=2010,inf=0x3f3f3f3f;
int fst[2*N],nxt[6*N],v[6*N],w[6*N],id[6*N],cnt=1;
int in[2*N],d[2*N],n;
queue<int> q;
vector<int> ans[N];
void add(int x,int y,int z,int i)
{
v[++cnt]=y,w[cnt]=z,id[cnt]=i;
nxt[cnt]=fst[x],fst[x]=cnt;
}
bool bfs()
{
while(!q.empty()) q.pop();
memset(d,0,sizeof(d));
q.push(0); d[0]=1;
while(!q.empty())
{
int x=q.front(); q.pop();
for(int i=fst[x];i;i=nxt[i])
{
int y=v[i];
if(d[y] || !w[i]) continue;
d[y]=d[x]+1; q.push(y);
if(y==n+1) return 1;
}
}
return 0;
}
int dinic(int x,int flow)
{
if(x==n+1) return flow;
int res=flow,tmp;
for(int i=fst[x];i && res;i=nxt[i])
{
int y=v[i];
if(d[y]!=d[x]+1 || !w[i]) continue;
tmp=dinic(y,min(res,w[i]));
if(!tmp) d[y]=0;
w[i]-=tmp,w[i^1]+=tmp,res-=tmp;
}
return flow-res;
}
int main()
{
int n1,n2,m,x,y,mi=inf;
scanf("%d%d%d",&n1,&n2,&m); n=n1+n2;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y); y+=n1;
add(x,y,1,i),add(y,x,0,-1);
in[x]++,in[y]++;
}
for(int i=1;i<=n;i++) mi=min(mi,in[i]);
for(int i=1;i<=n1;i++) add(0,i,in[i]-mi-1,-1),add(i,0,0,-1);
for(int i=n1+1;i<=n;i++) add(i,n1+n2+1,in[i]-mi-1,-1),add(n1+n2+1,i,0,-1);
for(int i=mi;i>=1;i--)
{
for(int j=cnt-1;j>=cnt-2*n+1;j-=2) w[j]++;
while(bfs())
while(dinic(0,inf));
for(int j=1;j<=cnt;j++)
if(id[j]!=-1 && w[j]) ans[i].pb(id[j]);
}
printf("0\n");
for(int i=1;i<=mi;i++)
{
int tmp=ans[i].size();
printf("%d ",tmp);
for(int j=0;j<tmp;j++) printf("%d ",ans[i][j]);
printf("\n");
}
return 0;
}