文章目录
- 题目描述
- 解析
- 代码
网络流要大胆建图
题目描述
公司内部共nnn个员工,员工之间可能有矛盾。若员工uuu和员工vvv有矛盾,用边(u,v)(u,v)(u,v)表示,共mmm个矛盾。
现在公司决定裁员,使得被裁人员间的矛盾指数最高。矛盾指数定义为被裁人员间的矛盾总数与被裁人员数的比值
解析
感觉很神仙的题
一开始自己的思路是暴力枚举裁员人数求最大矛盾,但是如何强制控制裁员人数把我卡住了…(费用流或许能做?)
最后还是看了题解qwq
关键是建图的方法
把每个职员和每条矛盾关系都看作一个点,那么(u,v)(u,v)(u,v)可以选取的前提就是uuu和vvv都选取
这样又成了求最大权闭合图的经典问题
但是如何确定点权呢?
考虑二分矛盾指数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
*/