[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(能过)。
代码:
可以假设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; }
相关文章推荐
- [拆边费用流] BZOJ 1449 [JSOI2009]球队收益
- 【bzoj1449】【JSOI2009】【球队收益】【费用流】
- 【bzoj1449/2895】 JSOI2009球队收益 费用流
- BZOJ 1449 JSOI2009 球队收益 费用流
- bzoj 1449 [JSOI2009]球队收益(费用拆分,最小费用流)
- bzoj 1449: [JSOI2009]球队收益 (费用流)
- BZOJ 1449 JSOI2009 球队收益 费用流
- bzoj 1449 [JSOI2009]球队收益(费用拆分,最小费用流)
- BZOJ 1449: [JSOI2009]球队收益 拆边费用流
- bzoj1449 [JSOI2009]球队收益
- BZOJ 1449/2895: [JSOI2009]球队收益|网络流
- BZOJ 1449: [JSOI2009]球队收益( 最小费用最大流)
- BZOJ 1449: [JSOI2009]球队收益 此坑必填 先去打cf
- Bzoj1449 [JSOI2009]球队收益
- bzoj1449 [JSOI2009]球队收益
- [网络流] bzoj1449: [JSOI2009]球队收益
- bzoj1449/2895[JSOI2009]球队收益
- BZOJ1449: [JSOI2009]球队收益
- 【BZOJ1449】[JSOI2009]球队收益【最小费用最大流】【单调增函数建图】
- BZOJ1449/2895: [JSOI2009]球队收益