CF976F Solution

题目链接

题解

因为度数最少为\(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;
}
posted @ 2021-08-17 14:16  violet_holmes  阅读(36)  评论(0编辑  收藏  举报