省选训练赛 #18 题目 D 补题记录
题意:有 \(n\) 棵待种的植物,关系呈一张 DAG,其中边 \((u, v)\) 表示必须等植物 \(u\) 成熟之后才能种下植物 \(v\),第 \(i\) 棵植物种下后需要花费 \(t_i\) 时间成熟。你有 \(m\) 点魔法,可以使用 \(d_i\) 点魔法令 \(t_i\) 减一,可以多次对一棵植物使用魔法,求最终种完所有植物的最早时间。
\(n, |E|\le 50,\ m,d_i\le 10^9,\ t_i \le 25\)
添加一个源植物 \(s\) 和一个汇植物 \(t\),前者需要种下后才能种其他植物,后者必须在所有植物种完后才能种下。设 \(x_i, y_i\) 表示植物 \(i\) 的种下时刻和成熟时刻,设 \(p_i\) 表示植物 \(i\) 的施法次数。二分答案 \(mid\),那么应该满足:
-
\(y_s + mid \ge x_t\)
-
\(x_i \le y_i\)
-
\(x_i + t_i - d_i \le y_i\)
-
\(x_v \ge y_u , \ \forall (u, v) \in E\)
不难发现这是最小费用循环流的对偶形式(详见此处):
-
\(0 - x_t + y_s \ge -mid\)
-
\(0 - x_i + y_i \ge 0\)
-
\(d_i - x_i + y_i \ge t_i\)
-
\(0 - y_u + x_v \ge 0, \ \forall (u, v) \in E\)
但是某些 \(d_{u, v}\) 必须取 \(0\),考虑答案式子 \(\sum_{(u, v) \in E} w_{u, v}d_{u, v}\) 中,令 \(w_{u, v} = + \infty\) 即可。
点击查看代码
#include <bits/stdc++.h>
namespace Initial {
#define ll long long
#define ull unsigned long long
#define fi first
#define se second
#define mkp make_pair
#define pir pair <ll, ll>
#define pb push_back
#define i128 __int128
using namespace std;
const ll maxn = 1050, inf = 1e13, mod = 1e9 + 7, L = 1e7 + 10;
ll power(ll a, ll b = mod - 2, ll p = mod) {
ll s = 1;
while(b) {
if(b & 1) s = 1ll * s * a %p;
a = 1ll * a * a %p, b >>= 1;
} return s;
}
template <class T>
const inline ll pls(const T x, const T y) { return x + y >= mod? x + y - mod : x + y; }
template <class T>
const inline void add(T &x, const T y) { x = x + y >= mod? x + y - mod : x + y; }
template <class T>
const inline void chkmax(T &x, const T y) { x = x < y? y : x; }
template <class T>
const inline void chkmin(T &x, const T y) { x = x > y? y : x; }
} using namespace Initial;
namespace Read {
char buf[1 << 22], *p1, *p2;
// #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, (1 << 22) - 10, stdin), p1 == p2)? EOF : *p1++)
template <class T>
const inline void rd(T &x) {
char ch; bool neg = 0;
while(!isdigit(ch = getchar()))
if(ch == '-') neg = 1;
x = ch - '0';
while(isdigit(ch = getchar()))
x = (x << 1) + (x << 3) + ch - '0';
if(neg) x = -x;
}
} using Read::rd;
ll n, t[maxn], c[maxn], a[maxn][maxn], m, deg[maxn], sum;
char str[maxn][maxn];
struct Graph {
ll n, s, t, head[maxn], tot, cur[maxn];
struct edge {ll v, w, c, nxt;} e[maxn];
void ins(ll u, ll v, ll w, ll c) {
e[++tot] = (edge) {v, w, c, head[u]}, head[u] = tot;
e[++tot] = (edge) {u, 0, -c, head[v]}, head[v] = tot;
}
bool vis[maxn]; ll dis[maxn]; queue <ll> q;
bool spfa() {
for(ll i = 1; i <= n; i++) dis[i] = inf;
dis[s] = 0, q.push(s);
while(!q.empty()) {
ll u = q.front(); q.pop(), vis[u] = false;
for(ll i = head[u]; i; i = e[i].nxt) {
ll v = e[i].v, w = e[i].w, c = e[i].c;
if(w && dis[v] > dis[u] + c) {
dis[v] = dis[u] + c;
if(!vis[v])
vis[v] = true, q.push(v);
}
}
} return dis[t] < inf;
}
ll dfs(ll u, ll flow) {
if(u == t) return flow;
ll used = 0; vis[u] = true;
for(ll i = cur[u]; i; cur[u] = i = e[i].nxt) {
ll v = e[i].v, w = e[i].w, c = e[i].c;
if(!vis[v] && w && dis[v] == dis[u] + c) {
ll tmp = dfs(v, min(w, flow - used));
used += tmp, e[i].w -= tmp, e[i ^ 1].w += tmp;
if(used == flow) break;
}
} vis[u] = false; return used;
}
pir dinic() {
ll f = 0, c = 0;
while(spfa()) {
for(ll i = 1; i <= n; i++) cur[i] = head[i];
f += dfs(s, inf);
}
for(ll i = 3; i <= tot; i += 2) c -= e[i].w * e[i].c;
return mkp(f, c);
}
void clr() {
for(ll i = 1; i <= n; i++) head[i] = 0;
tot = 1, n = s = t = 0;
}
} G;
bool check(ll mid) {
G.clr(), G.ins(2 * n + 3, n + 2, inf, mid); sum = 0;
for(ll i = 1; i <= 2 * n + 4; i++) deg[i] = 0;
for(ll i = 1; i <= n; i++) {
G.ins(i + n + 2, i, inf, 0), G.ins(i, i + n + 2, c[i], t[i]);
deg[i + n + 2] += c[i], deg[i] -= c[i];
sum += c[i] * t[i];
}
for(ll i = 1; i <= n; i++)
G.ins(i, 2 * n + 3, inf, 0), G.ins(n + 2, i + n + 2, inf, 0);
for(ll u = 1; u <= n; u++)
for(ll v = 1; v <= n; v++)
if(a[u][v]) G.ins(v, u + n + 2, inf, 0);
ll S = 2 * n + 5, T = S + 1, g = 0;
G.n = G.t = T, G.s = S;
for(ll i = 1; i <= 2 * n + 4; i++)
if(deg[i] < 0) G.ins(S, i, -deg[i], 0), g -= deg[i];
else if(deg[i] > 0) G.ins(i, T, deg[i], 0);
pir res = G.dinic();
return res.fi == g && sum - res.se <= m;
}
int main() {
rd(n);
for(ll i = 1; i <= n; i++) {
scanf("%s", str[i] + 1);
for(ll j = 1; j <= n; j++)
if(str[i][j] == '1') a[j][i] = 1;
} rd(n);
for(ll i = 1; i <= n; i++) rd(t[i]);
rd(n);
for(ll i = 1; i <= n; i++) rd(c[i]);
rd(m);
ll lo = 0, hi = 1250;
while(lo <= hi) {
ll mid = lo + hi >> 1;
if(check(mid)) hi = mid - 1;
else lo = mid + 1;
} printf("%lld\n", lo);
return 0;
}