您的位置:首页 > Web前端 > JavaScript

[BZOJ1449][JSOI2009]球队收益(费用流)

2018-03-17 12:14 302 查看
由于打完一场比赛后胜方和负方都有收益,因此直接做不好做。

可以假设mm场比赛双方都输,然后统计收益。假设这时候第ii对胜wiwi场,输lili场。

然后就相当于对mm场比赛都选择一个队作为胜方,这样就使得只有胜方有收益了。

这时候如果最后第ii个队胜了jj场,那么第ii个队的实际收益将是:

Ci×(wi+j)2+Di×(li−j)2Ci×(wi+j)2+Di×(li−j)2

我们知道,假设mm场比赛双方都输时,第ii个队的收益是Ci×w2i+Di×l2iCi×wi2+Di×li2。

增量为:

Ci×(wi+j)2+Di×(li−j)2−Ci×w2i−Di×l2iCi×(wi+j)2+Di×(li−j)2−Ci×wi2−Di×li2

=Ci×(2×wi×j+j2)+Di×(−2×li×j+j2)=Ci×(2×wi×j+j2)+Di×(−2×li×j+j2)

=2×(Ci×wi−Di×li)×j+(Ci+Di)×j2=2×(Ci×wi−Di×li)×j+(Ci+Di)×j2

建一个费用流的模型:

mm个点X1,X2,...,XmX1,X2,...,Xm表示mm场比赛;

nn个点Y1,Y2,...,YnY1,Y2,...,Yn表示nn支球队;

源点和汇点S,TS,T。

建图:

(1)由SS向所有的XiXi建一条容量为11,费用为00的边,表示一场比赛只有一个胜方;

(2)对于所有的1≤i≤m1≤i≤m:

<1>由XiXi向YaiYai建一条容量为11,费用为00的边。

<2>由XiXi向YbiYbi建一条容量为11,费用为00的边。

易得,这样流入YiYi的流量就是第ii支球队在mm次比赛中的胜场次数。

(3)由所有的YiYi向TT建一条边,容量为YiYi的入度

费用costcost与这条边的流量xx的关系式为:

cost=2×(Ci×wi−Di×li)×x+(Ci+Di)×x2cost=2×(Ci×wi−Di×li)×x+(Ci+Di)×x2

关键问题在于costcost是xx的二次函数,不能直接建边。

而对于任意正整数xx,有:

1+3+5+7+...+(2x−1)=x21+3+5+7+...+(2x−1)=x2

因此如果要建一条边,使这条边的费用等于通过这条边流量的二次方,就可以:

把容量为cc的边拆成cc条容量为11的边,第ii条边的单位费用为2i−12i−1。

就可以满足条件。

(3)中也可以这样拆边,设YiYi的入度为cc,就可以:

由YiYi向TT建cc条边,容量为11,第jj条边的单位费用为:

2×(Ci×wi−Di×li)+(Ci+Di)×(2×j−1)2×(Ci×wi−Di×li)+(Ci+Di)×(2×j−1)

这样就达到了目的。

时间复杂度O(能过)O(能过)。

代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
inline int read()
{
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
whil
9444
e ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
const int N = 4e5 + 5, INF = 0x3f3f3f3f;
int n, m, win
, lose
, C
, D
, A
, B
, ecnt = 1, nxt
,
adj
, go
, cap
, cost
, S, T, in
, dis
, len,
que[N << 2], ans;
bool vis
, orz
;
void add_edge(int u, int v, int w, int x)
{
nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v;
cap[ecnt] = w; cost[ecnt] = x;
nxt[++ecnt] = adj[v]; adj[v] = ecnt; go[ecnt] = u;
cap[ecnt] = 0; cost[ecnt] = -x;
}
bool spfa()
{
for (int i = S; i <= T; i++) dis[i] = INF, vis[i] = orz[i] = 0;
dis[que[len = 1] = S] = 0;
for (int i = 1; i <= len; i++)
{
int u = que[i]; vis[u] = 0;
for (int e = adj[u], v; e; e = nxt[e])
if (cap[e] && dis[u] + cost[e] < dis[v = go[e]])
{
dis[v] = dis[u] + cost[e];
if (!vis[v]) vis[que[++len] = v] = 1;
}
}
return dis[T] < INF;
}
int dfs(int u, int flow)
{
if (u == T) return ans += flow * dis[T], flow;
orz[u] = 1;
int res = 0, delta;
for (int e = adj[u], v; e; e = nxt[e])
if (cap[e] && !orz[v = go[e]] && dis[u] + cost[e] == dis[v])
{
delta = dfs(v, min(cap[e], flow - res));
if (delta)
{
cap[e] -= delta; cap[e ^ 1] += delta;
res += delta; if (res == flow) break;
}
}
return res;
}
int mcmf()
{
ans = 0;
while (spfa()) dfs(S, INF);
return ans;
}
int main()
{
n = read(); m = read();
for (int i = 1; i <= n; i++)
win[i] = read(), lose[i] = read(),
C[i] = read(), D[i] = read();
for (int i = 1; i <= m; i++)
lose[A[i] = read()]++,
lose[B[i] = read()]++;
S = 1; T = n + m + 2;
for (int i = 2; i <= m + 1; i++)
add_edge(S, i, 1, 0);
for (int i = 1; i <= m; i++)
add_edge(i + 1, A[i] + m + 1, 1, 0),
add_edge(i + 1, B[i] + m + 1, 1, 0),
in[A[i]]++, in[B[i]]++;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= in[i]; j++)
add_edge(i + m + 1, T, 1,
(C[i] * win[i] - D[i] * lose[i] << 1) +
(C[i] + D[i]) * ((j << 1) - 1));
int sum = 0;
for (int i = 1; i <= n; i++)
sum += C[i] * win[i] * win[i] + D[i] * lose[i] * lose[i];
cout << sum + mcmf() << endl;
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: