bzoj 4334 铁拳
Description
经过了可怕的第三次世界大战后,国家政府崩溃,各大财团趁机夺取掌控世界。长年战争后,八大财团幸存并割据一方,其中最强的当属掌控北美的铁拳。
在铁拳财团所维护的文明区域中,有一项最为光荣、重要的赛事—— Iron Fist ,也就是铁拳大赛。 IF 中云集了世界各地各财团鼎力资助的世外高手,只为了赢得 IF Champion,得到无上的荣耀,当然还有随之而来的权力。本来一切秩序井然,但一个来自贫民窟的少年风间仁意外地在海选中赢了 IF 正式选手,获得了决赛资格,从此格局被打乱……
为了应对这突如其来的变数,IF 管理层决定先对联盟中所有的选手进行评估,以更好地掌握大局。
已知最近 \(m\) 届比赛出现过的 \(n\) 位选手,背后都有着各自财团的资助,并且签下了合同。由于这是各财团的高度机密,合同的具体细节无从得知,但铁拳财团的间谍们通过各种渠道得知了每个选手的薪金范围(显然薪金是非负数)。
对于最近 \(m\) 届的 IF 比赛(从 \(1\) 开始编号),每一届联盟都会进行清算,通过国际金融手段准确计算出这一届联盟选手身价总和的变化。每一届中,会有一些新选手加入,也会有部分选手在比赛中丧失了战斗能力,而被踢出联盟,流放到贫民窟。
现在给出联盟中 \(n\) 位选手的身价范围,以及他们 进入联盟的届数( \(0\) 表示在 \(m+1\) 届以前就已经是联盟选手) 和 离开联盟的届数( \(0\) 表示是现役选手)。同时给出最近 \(m\) 届中,每一届联盟选手身价总和减去上一届的值。
请你根据现有信息,尽可能准确地给出每个选手可能的薪金范围。各选手之间的薪金范围可以不同时成立,但对于一位选手的范围中的每一个数,都必须至少存在一种合法方案使该选手能得到相应薪金,而且这个范围跨度要尽可能大。
如果输入信息有误,请输出 \(-1\) ,表示无解。
Input
第一行一个正整数 \(m\) ,意义见上(下同)。
第二行包含 \(m\) 个整数,第i个表示第 \(i\) 届中 选手身价总和 的变化情况。
第三行一个正整数 \(n\) 。
接下来 \(n\) 行,每行包含四个整数,分别表示 身价下限 、 身价上限 、 出道届数 、 退役届数,细节请参照上文。
保证出道时间严格比退役时间小( \(0\) 除外)。
Output
如果输入信息有误,仅输出一行一个整数“-1”(不含双引号)。
否则输出 \(n\) 行,每行两个实数,第 \(i\) 行表示第 \(i\) 个选手实际身价的准确范围。
保留两位小数,需要与标准答案完全相同才得分。
Sample Input
2
5 ‐1
3
1 4 1 0
2 3 1 0
1 5 1 2
Sample Output
1.00 2.00
2.00 3.00
1.00 1.00
HINT
Explanation
第二届只有 \(3\) 号离开了,可以锁定 \(3\) 号的薪金是 \(1\) 。
如此一来,\(1\) 号和 \(2\) 号薪金之和为 \(4\) ,那么 \(1\) 号最少能拿 \(1\) ,最多能拿 \(2\) ;\(2\) 号最少能拿到 \(2\) ,最多能拿到 \(3\) 。
Data
对于 \(100\%\) 的数据, \(n\le 200,m\le 100\) ,给定薪金范围不超过 \(20000\) 。
Solution
今天下午被 Joker 和 Azrael_Death 教导了好久才开窍。
线性规划,用上下界网络流来做。
翻译一下题:给定 \(n\) 个未知数,以及 \(m\) 条方程,其中未知数系数绝对值为 \(1\) ,保证等式之间没有相同的单项。再给出每个未知数的范围约束。求每个未知数的取值范围。
首先我们假看到这篇博客的人都会上下界网络流。
我们把每个方程看成点,每个未知数看成边。我们把第 \(i\) 个方程的值记作 \(gap_i\),第 \(i\) 个未知数的取值范围、进/出方程位置分别记作 \(lw_i,up_i,L_i,R_i\) 。 显然每个未知数只会进出方程各一次,所以未知数 \(i\) 所表示的边有且仅有一条。
设源、汇、超级源、超级汇分别为 \(SS, TT, S, T\) 。
对于未知数 \(i\) ,若 \(L_i=0\) 则记 \(u = SS\) ,否则 \(u=L_i\) ;若 \(R_i=0\) 则记 \(v = TT\) ,否则 \(v=R_i\) 。那么我们有 $$ <u,v>:capacity=[lw_i,up_i] $$
对于方程 \(i\) ,若 \(gap_i > 0\) ,则 \(u = SS\) ,\(v=i\) ;否则 \(u = i\) , \(v=TT\) 。我们有 $$ <u,v>:capacity=[|gap_i|,|gap_i|] $$
然后我们尝试跑一个可行流。若没有,直接输出 \(-1\) 。若有,我们将在残量网络上求出答案。考虑每条边可以再多/少承载多少流量。方法是干掉这条边,以起点为源,终点为汇,跑最大流;然后以起点为汇,终点为源,再跑最大流。因为有 \([lw_i,up_i]\) 的限制,所以要处理一下。具体见代码。
#include<bits/stdc++.h>
using namespace std;
#define N 250
#define INF 2000000000
#define rep(i, a, b) for (int i = a; i <= b; i++)
inline int read() {
int x = 0, flag = 1; char ch = getchar(); while (!isdigit(ch)) { if (!(ch ^ '-')) flag = -1; ch = getchar(); }
while (isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar(); return x * flag;
}
int n, m;
int gap[N], up[N], lw[N], L[N], R[N];
int du[N], mch[N];
int S, T, SS, TT;
struct edge {
int v, c, next;
edge(int _v = 0, int _c = 0, int _next = 0):v(_v), c(_c), next(_next) {}
}e[1001], cpy[1001];
int head[N], tot = 1, tag[1001];
int q[N], dep[N];
inline void insert(int u, int v, int c) { e[++tot] = edge(v, c, head[u]), head[u] = tot; }
inline void add(int u, int v, int c) { insert(u, v, c), insert(v, u, 0); }
inline bool bfs() {
memset(dep, 0, sizeof dep); dep[S] = 1;
int l = 1, r = 1; q[1] = S;
while (l <= r) {
int u = q[l++];
for (int i = head[u], v; i; i = e[i].next) if (!tag[i] && e[i].c && !dep[v = e[i].v]) {
dep[v] = dep[u] + 1, q[++r] = v;
if (!(v ^ T)) return 1;
}
}
return 0;
}
int dfs(int u, int dist) {
if (!(u ^ T) || !dist) return dist;
int ret = 0;
for (int i = head[u], v, c; i; i = e[i].next) if (!tag[i] && (c = e[i].c) && !(dep[v = e[i].v] ^ (dep[u] + 1))) {
int d = dfs(v, min(dist, c));
dist -= d, ret += d, e[i].c -= d, e[i ^ 1].c += d;
if (!dist) break;
}
return ret;
}
inline int dinic() { int ret = 0; while (bfs()) ret += dfs(S, INF); return ret; }
bool getAva() {
rep(i, 1, m) {
if (gap[i] > 0) du[SS] -= gap[i], du[i] += gap[i];
else du[i] += gap[i], du[TT] -= gap[i];
}
rep(i, 1, n) {
int u = L[i] ? L[i] : SS, v = R[i] ? R[i] : TT;
add(u, v, up[i] - lw[i]), du[u] -= lw[i], du[v] += lw[i], mch[i] = tot - 1;
}
add(TT, SS, INF);
int sum1 = 0, sum2 = 0;
rep(i, SS, TT) if (du[i] >= 0) add(S, i, du[i]), sum1 += du[i]; else add(i, T, -du[i]), sum2 -= du[i];
return dinic() == sum1 && sum1 == sum2;
}
inline void reset() { rep(i, 0, tot) e[i] = cpy[i]; }
int main() {
cin >> m; rep(i, 1, m) gap[i] = read();
cin >> n; rep(i, 1, n) lw[i] = read(), up[i] = read(), L[i] = read(), R[i] = read();
TT = m + 1, S = TT + 1, T = S + 1;
if (!getAva()) { puts("-1"); return 0; }
rep(i, 0, tot) cpy[i] = e[i];
rep(i, 1, n) {
int id = mch[i], t1 = e[id ^ 1].c, t2 = e[id].c + t1; tag[id] = tag[id ^ 1] = 1;
S = e[id ^ 1].v, T = e[id].v, printf("%d.00 ", lw[i] + max(t1 - dinic(), 0)), reset();
S = e[id].v, T = e[id ^ 1].v, printf("%d.00\n", lw[i] + min(t1 + dinic(), t2)), reset();
tag[id] = tag[id ^ 1] = 0;
}
return 0;
}