网络流要大胆建图
题目描述
公司内部共 n n n个员工,员工之间可能有矛盾。若员工 u u u和员工 v v v有矛盾,用边 ( u , v ) (u,v) (u,v)表示,共 m m m个矛盾。
现在公司决定裁员,使得被裁人员间的矛盾指数最高。矛盾指数定义为被裁人员间的矛盾总数与被裁人员数的比值
解析
感觉很神仙的题
一开始自己的思路是暴力枚举裁员人数求最大矛盾,但是如何强制控制裁员人数把我卡住了…(费用流或许能做?)
最后还是看了题解qwq
关键是建图的方法
把每个职员和每条矛盾关系都看作一个点,那么
(
u
,
v
)
(u,v)
(u,v)可以选取的前提就是
u
u
u和
v
v
v都选取
这样又成了求最大权闭合图的经典问题
但是如何确定点权呢?
考虑二分矛盾指数g,考虑是否存在一种方案,使得:
边
数
/
点
数
>
=
g
边数/点数>=g
边数/点数>=g
移项,得:
边
数
∗
1
+
点
数
∗
(
−
g
)
>
=
0
边数*1+点数*(-g)>=0
边数∗1+点数∗(−g)>=0
这样就可以直接上dinic求了
代码
#include<bits/stdc++.h>
#define ll double
using namespace std;
const int N=5050;
const int M=1e9;
ll read(){
ll x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-') f=-1;c=getchar();};
while(isdigit(c)){x=x*10+c-'0';c=getchar();}
return x*f;
}
int n,m,s,t;
struct node{
int to,nxt;ll cap;
}p[300500];
int fi[N],cnt;
void addline(int x,int y,ll cap){
p[++cnt]=(node){y,fi[x],cap};fi[x]=cnt;
p[++cnt]=(node){x,fi[y],0};fi[y]=cnt;
}
int col[N],cur[N];
queue<int>q;
int bfs(){
memset(col,0,sizeof(col));col[s]=1;
q.push(s);
while(!q.empty()){
int now=q.front();q.pop();
for(int i=cur[now]=fi[now];~i;i=p[i].nxt){
int to=p[i].to;
if(col[to]||!p[i].cap) continue;
col[to]=col[now]+1;
q.push(to);
}
}
return col[t];
}
ll dfs(int x,ll lim){
if(x==t||!lim) return lim;
ll res=0;
for(int &i=cur[x];~i;i=p[i].nxt){
int to=p[i].to;
if(!p[i].cap||col[to]!=col[x]+1) continue;
ll add=dfs(to,min(lim,p[i].cap));
lim-=add;res+=add;
p[i].cap-=add;p[i^1].cap+=add;
if(!lim) break;
}
if(lim) col[x]=-1;
return res;
}
ll dinic(){
ll res=0;
while(bfs()){
while(ll tmp=dfs(s,2e15)) res+=tmp;
}
return res;
}
int vis[N];
void find1(int x){
if(vis[x]) return;
vis[x]=1;
for(int i=fi[x];~i;i=p[i].nxt){
if(!p[i].cap) continue;
find1(p[i].to);
}
return;
}
void find2(int x){
if(vis[x]) return;
vis[x]=2;
for(int i=fi[x];~i;i=p[i].nxt){
if(!p[i^1].cap) continue;
find2(p[i].to);
}
return;
}
int num;
int u[N],v[N];
bool check(double x){
memset(fi,-1,sizeof(fi));cnt=-1;
double tot=m;
s=n+m+1;t=n+m+2;
for(int i=1;i<=n;i++){
addline(i,t,x);
}
for(int i=1;i<=m;i++){
addline(s,i+n,1);
addline(i+n,u[i],2e15);
addline(i+n,v[i],2e15);
}
return tot-dinic()>0;
}
int main(){
n=read();m=read();
if(!m){
printf("1\n1");return 0;
}
for(int i=1;i<=m;i++){
u[i]=read();v[i]=read();
}
double st=0,ed=2e9;
for(int i=1;i<=100;i++){
double mid=(st+ed)/2;
if(check(mid)) st=mid;
else ed=mid;
}
check(st);
find1(s);
int num=0;
for(int i=1;i<=n;i++){
num+=vis[i]?1:0;
}
printf("%d\n",num);
for(int i=1;i<=n;i++){
if(vis[i]) printf("%d\n",i);
}
return 0;
}
/*
3 2 1 3
1 2 10
2 3 10
*/