2324. 生活的艰辛

题目链接

2324. 生活的艰辛

约翰是一家公司的 CEO。

公司的股东决定让他的儿子斯科特成为公司的经理。

约翰十分担心,儿子会因为在经理岗位上表现优异而威胁到他 CEO 的位置。

因此,他决定精心挑选儿子要管理的团队人员,让儿子知道社会的险恶。

已知公司中一共有 \(n\) 名员工,员工之间共有 \(m\) 对两两矛盾关系。

如果将一对有矛盾的员工安排在同一个团队,那么团队的管理难度就会增大。

一个团队的管理难度系数等于团队中的矛盾关系对数除以团队总人数。

团队的管理难度系数越大,团队就越难管理。

约翰希望给儿子安排的团队的管理难度系数尽可能大。

请帮帮他。

image

以上图为例,管理难度系数最大的团队由 \(1,2,4,5\) 号员工组成,他们 \(4\) 人中共有 \(5\) 对矛盾关系,所以管理难度系数为 \(\frac{5}{4}\)

如果我们将 \(3\) 号员工也加入到团队之中,那么管理难度系数就会降至 \(\frac{6}{5}\)

输入格式

第一行包含两个整数 \(n\)\(m\)

接下来 \(m\) 行,每行包含两个整数 \(a_i\)\(b_i\),表示员工 \(a_i\)\(b_i\) 之间存在矛盾。

所有员工编号从 \(1\)\(n\)

每个矛盾对最多在输入中出现一次,且介绍矛盾对时,员工介绍顺序是随意的。

输出格式

首先输出一个整数 \(k\),表示安排给斯科特的团队人员数量。

接下来 \(k\) 行,以升序输出团队每个成员的编号,每行一个。

如果答案不唯一,则输出任意一种即可。

注意:至少要选择一名员工。

数据范围

\(1 \le n \le 100\),
\(0 \le m \le 1000\),
\(1 \le k \le n\)

输入样例1:

5 6
1 5
5 4
4 2
2 5
1 2
3 1

输出样例1:

4
1
2
4
5

输入样例2:

4 0

输出样例2:

1
1

提示

注意样例 \(2\) 中,任意团队的管理困难系数都是 \(0\),这种情况输出任意非空方案即可。

解题思路

最小割,最大密度子图,分数规划

\(G\) 中选择一个点集 \(V\) 和边集 \(E\),其中边集的任意一条边的两个端点都要求属于 \(V\),求 \(\frac{|E|}{|V|}\) 的最大值,其中 \(|E|\) 为边数,\(|V|\) 为点数
\(\frac{|E|}{|V|}>g\Leftrightarrow |E|-g\times |V|>0\),如果 \(max\{|E|-g\times |V|\}>0\),说明答案应该会比 \(g\) 更大,即该式子具有单调性,可以二分
要求 \(|E|-g\times |V|\) 最大,即 \(g\times |V|-|E|\) 最小,假设选择的点集为 \(V'\),边集为 \(E'\),则 \(g\times |V'|-|E'|=\sum\limits_{v\in V'}g-|E'|\),设 \(d[i]\)\(i\) 的度数,对于选择的点集 \(V'\),其如果两个点之间有边的话一定在 \(E'\) 中更优,设 \(c[V,V']\) 表示点集 \(V\) 和 点集 \(V'\) 之间边的数量,则不难发现 \(|E'|=\frac{\sum\limits_{v\in V'}d[v]-c[V',V-V']}{2}\)\(g\times |V'|-|E'|=\sum\limits_{v\in V'}g-\frac{\sum\limits_{v\in V'}d[v]-c[V',V-V']}{2}=\frac{1}{2}\times(\sum\limits_{v\in V'}(2g-d[v])+c[V',V-V'])\),其中显然 \(c[V',V-V']\) 当原图对应流网络的边容量都为 \(1\) 时可以对应流网络中的一个割 \([S,T]\),而还存在 \(2g-d[v]\) 这个点权,不妨将所有点向汇点连一条容量为 \(2g-d[v]\) 的边,这样 \(V'=S-{s}\) 中的每个点都会向 \(T\) 中的 \(t\) 连一条容量为 \(2g-d[v]\) 的割边,但是流网络的所有容量都要求非负,而 \(2g-d[v]\) 可能为负,不妨将所有点连向汇点 \(t\) 的容量加上一个偏移量 \(U\),同时源点 \(s\) 向所有点连一条容量为 \(U\) 的边,\(\color{red}{为什么?}\)此时割边分为三部分:\(s->V-V',V'->t,V->V-V'\),则 \(C[S,T]=\sum\limits_{v\in V-V'}U+\sum\limits_{v\in V'}(2g-d[v]+U)+\sum\limits_{v\in V'}\sum\limits_{u\in V-V'}c[v,u]=nU+\sum\limits_{v\in V'}(2g-(d[v]-\sum\limits_{u\in V-V'}c[v,u])=nU+\sum\limits_{v\in V'}(2g-\sum\limits_{u\in V'}c[v,u])=nU+2g|V'|-2|E'|\),要使 \(g|V'|-|E'|\) 最小,即 \(C[S,T]\) 最小,即最小割
另外,由于 \(d[v]\) 不可能超过边数,即可以将 \(U\) 置为边数

本题是模板题,另外,本题要求输出方案,由 2171. EK求最大流 可知,证明最小割最大流定理时,构造的正是残余网络中从 \(s\) 开始沿着流量大于 \(0\) 的边走,将所有这样的点即为 \(S\) 中除 \(s\) 的部分,即本题直接从 \(s\) 开始 dfs 即可得到方案

扩展
图中存在边权
即要求 \(\frac{|E|}{|V|}\) 最大,其中 \(|E|\) 为所有选择的边权之和,\(|V|\) 为所有选择的点的数量,则可以在上述建图稍微改动一下:将 \(d[v]\) 改成所有从 \(v\) 出发的边权之和,不难发现答案依然如此
图中存在点权和边权
即要求 \(\frac{|E|+|V|}{cnt_V}\) 最大,其中 \(|E|\) 为所有选择的边权之和,\(|V|\) 为所有选择的点权之和,\(cnt_V\) 为点集 \(V\) 的点数,此时要最大化 \(|E|+|V|-g\times cnt_V\),等价于最小化 \(g\times cnt_V-|E|-|V|\),该等式按照上面的证明即 \(\frac{1}{2}\times(\sum\limits_{v\in V'}(2g-d[v]-2p_v)+c[V',V-V'])\),其中 \(p_v\)\(v\) 点权值,\(c[u,v]\)\(u\)\(v\) 的边权,即在上面存在边权的做法下,向汇点连的边的容量应该为 \(2g-d[v]-2p_v\)

  • 时间复杂度:\(O(n^2mlogw)\)

代码

// Problem: 生活的艰辛
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/description/2326/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%Skyqwq
#include <bits/stdc++.h>
 
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

const int N=105,M=(2*N+1005)*2;
const double inf=1e9;
int n,m,S,T;
int h[N],ne[M],e[M],idx;
double f[M],deg[N];
PII edge[M];
int d[N],hh,tt,q[N],cur[N];
bool v[N];
int res;
void add(int a,int b,double c1,double c2)
{
	e[idx]=b,f[idx]=c1,ne[idx]=h[a],h[a]=idx++;
	e[idx]=a,f[idx]=c2,ne[idx]=h[b],h[b]=idx++;
}
void build(double x)
{
	memset(h,-1,sizeof h);
	idx=0;
	for(int i=1;i<=m;i++)add(edge[i].fi,edge[i].se,1,1);
	for(int i=1;i<=n;i++)
	{
		add(S,i,m,0);
		add(i,T,2*x-deg[i]+m,0);
	}
}
bool bfs()
{
	memset(d,-1,sizeof d);
	d[S]=hh=tt=0;
	q[0]=S;
	cur[S]=h[S];
	while(hh<=tt)
	{
		int x=q[hh++];
		for(int i=h[x];~i;i=ne[i])
		{
			int y=e[i];
			if(d[y]==-1&&f[i]>0)
			{
				d[y]=d[x]+1;
				cur[y]=h[y];
				if(y==T)return true;
				q[++tt]=y;
			}
		}
	}
	return false;
}
double dfs(int x,double limit)
{
	if(x==T)return limit;
	double flow=0;
	for(int i=cur[x];~i&&flow<limit;i=ne[i])
	{
		cur[x]=i;
		int y=e[i];
		if(d[y]==d[x]+1&&f[i]>0)
		{
			double t=dfs(y,min(f[i],limit-flow));
			if(t<=0)d[y]=-1;
			f[i]-=t,f[i^1]+=t,flow+=t;
		}
	}
	return flow;
}
double dinic()
{
	double res=0,flow;
	while(bfs())while((flow=dfs(S,inf))>0)res+=flow;
	return res;
}
bool ck(double x)
{
	build(x);
	return (double)n*m-dinic()>0;
}
void dfs(int x)
{
	res+=(x!=S);
	v[x]=true;
	for(int i=h[x];~i;i=ne[i])
		if(f[i]>0&&!v[e[i]])dfs(e[i]);
}
int main()
{
    scanf("%d%d",&n,&m);
    S=0,T=n+1;
    for(int i=1;i<=m;i++)
    	scanf("%d%d",&edge[i].fi,&edge[i].se),deg[edge[i].fi]++,deg[edge[i].se]++;
    double l=0,r=m;
    while(r-l>1e-8)
    {
    	double mid=(l+r)/2;
    	if(ck(mid))l=mid;
    	else
    		r=mid;
    }
    ck(l);
	dfs(S);
	if(!res)puts("1\n1");
	else
	{
		printf("%d\n",res);
		for(int i=1;i<=n;i++)
			if(v[i])printf("%d\n",i);
	}
    return 0;
}
posted @ 2022-11-30 11:43  zyy2001  阅读(49)  评论(0编辑  收藏  举报