最大流,二分
题意:
分析:
这一题,我刚拿到手是蒙B的。我的第一反应也是最小费用最大流,但是想了一会也是不知道该如何表示X^2这个万恶的东西。
迫不得已,展开别的思路:
我们这样建图:
源点S与1~n相连,取cap = 1 无cost
1~n与n+1 ~ n+m 按照数据相连 cap = 1,无cost
n+1 ~ n+m 与t 相连,无cost, cap待定
观察函数 X^2 导数2X x越大他增的越厉害。这是常识
那么,在我们建的图中,最终在n+1~n+m向t汇聚时,我们要尽量避免某一条路径的x特别的大。
因为总值我们是知道的为n
那么最好他们都是相等的,为 n/m 这样最小!!
即便无法相等,我们也要尽量使得n能够均摊到每一条路径上。
这时我想到了二分法。
我们在 n+1 ~ n+m 与t相连边的cap上二分。
如果当前的cap能够使得最大流为n。
那么我就减小cap,遏制特别突出的,使他们均摊。
如果当前cap不能够使得最大流为n <n
那么我就增加cao,稍微放宽一点。
这样我们得到了边缘状态:res
即cap为res时最大流为n,cap为res-1时最大流<n
似乎,res就是正解。
我们在遍历每一个n+1 ~ n+m的边,看当前边通过的流量(其反边的cap)然后平方和相加就行了.
但是,这其实是有疏漏的。
我们注意到当res-1时,所有的流通路径都是绷紧的,大家都尽己所能地传送。
这时候毫无疑问时完美均摊的。
但是最大流没达标,所以我们cap+1变为res.
但是这样做之后,我们就无法保证后面的这些路径那些是绷紧的那些是松弛的。
比如:
n=11
res-1 = 3
第一条路经流量:2
第二条路径流量:2
第三条路径流量:3
第四条路径流量:3
放宽变为res=4
第一条路经流量:1
第二条路径流量:2
第三条路径流量:4
第四条路径流量:4
我们发现第一条路经竟然减1了,而这减的1加到了第四条路径上了。
我们很明显就知道这样是不划算的,
正确的应该是:
第一条路经流量:2
第二条路径流量:2
第三条路径流量:4
第四条路径流量:3
如此才对。
那么我们如何操作呢。
很简单,我们不去计算res的情况,我们去计算res-1时的情况。
看当前最大流与n相差多少,假设为cnt
那么我们在遍历n+1~n+m与 t 的边时,如果发现其流量为res-1且cnt>0
那么我们就把这条边的流量加一 再平方相加,然后cnt-=1
此为正解!!
代码如下:
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
const int inf = 2e9;
const int max_n = 55;
const int max_m = 55*55;
int n, m;
struct edge
{
int to, cap, rev, next;
}E[max_m * 2];
int head[max_n * 2];
int cnt = 1;
void add(int from, int to, int cap) {
E[cnt].to = to;
E[cnt].cap = cap;
E[cnt].rev = cnt + 1;
E[cnt].next = head[from];
head[from] = cnt++;
E[cnt].to = from;
E[cnt].cap = 0;
E[cnt].rev = cnt - 1;
E[cnt].next = head[to];
head[to] = cnt++;
}
void init() {
fill(head, head + max_n * 2, false);
cnt = 1;
}
int iter[max_n * 2];
int dist[max_n * 2];
bool searchpath(int s, int t) {
fill(dist, dist + max_n * 2, -1);
queue<int> que;dist[s] = 0;
que.push(s);
while (!que.empty()) {
int u = que.front();que.pop();
for (int i = head[u];i;i = E[i].next) {
edge& e = E[i];
if (dist[e.to] != -1 || e.cap == 0)continue;
dist[e.to] = dist[u] + 1;
que.push(e.to);
}
}return dist[t] != -1;
}
int matchpath(int u, int t, int f) {
if (u == t)return f;
for (int& i = iter[u];i;i = E[i].next) {
edge& e = E[i];
if (dist[e.to] <= dist[u] || e.cap <= 0)continue;
int d = matchpath(e.to, t, min(e.cap, f));
if (d > 0) {
e.cap -= d;
E[e.rev].cap += d;
return d;
}
}return false;
}
int dinic(int s, int t) {
int flow = 0;int f;
while (searchpath(s, t)) {
for (int i = 1;i < max_n * 2;i++)iter[i] = head[i];
while (f = matchpath(s, t, inf))
flow += f;
}return flow;
}
int a[max_n], b[max_n];
int check(int mid,int type = 0) {
init();
int start = n + m + 1;
int ed = start + 1;
for (int i = 1;i <= n;i++) {
add(start, i, 1);
add(i, a[i] + n, 1);
add(i, b[i] + n, 1);
}for (int i = 1;i <= m;i++)
add(i + n, ed, mid);
return n - dinic(start, ed);
}
int ef(int tot) {
int left = 1;int right = tot;
while (left < right) {
int mid = (left + right) >> 1;
if (check(mid) == 0)right = mid;
else left = mid + 1;
}
int acnt = check(right - 1);
int res = 0;
for (int i = 1;i <= m;i++) {
if (!head[i + n])continue;
edge& e = E[head[i + n]];
if (E[e.rev].cap == right - 1 && acnt) {
res += 2 * E[e.rev].cap + 1;
acnt--;
}
res += E[e.rev].cap * E[e.rev].cap;
}
return res;
}
int main() {
ios::sync_with_stdio(0);
cin >> n >> m;
for (int i = 1;i <= n;i++)cin >> a[i] >> b[i];
cout << ef(n) << endl;
}
京公网安备 11010502036488号