题目
思路
直接讲吧。
- 限制每天的 a i ai ai : 我们将学生分为两类,濒死的学生归在 S S S一边,活着的学生归在 T T T一边。对于每一天,我们将第 i i i天拆成 i x ix ix 和 i y iy iy。 i x ix ix为濒死的, i y iy iy为活着的。把 S S S向 i x ix ix连流量为 a i ai ai,费用为0的边,把 i y iy iy向 T T T连流量为 a i ai ai,费用为0的边。
- 对于学校的提供 : 对于一个学校 j j j, 可将它看做一个节点,例如标号为 2 ∗ n + j 2 * n + j 2∗n+j, 然后 S S S向这个点连流量为 l j lj lj,费用为0的边,这个点再向 i y iy iy连一条流量为INF,费用为 p j pj pj的边。就完成了虚拟濒死的人通过学校复活,即学校提供学生的过程。
- 对于濒死的人,在第 i i i天不一定要将他复活,可以留到下一天再复活,所以将濒死一边的 i i i号点和 i + 1 i+1 i+1号点连一条流量为 I N F INF INF, 费用为0的边,将死的人留到下一天复活。
- 至于第 j j j家医院复活濒死的人,如果这是第 i i i天,并且 i x + d j + 1 < = n ix + dj + 1 <= n ix+dj+1<=n,那么可以将他在 i y + d j + 1 iy + dj + 1 iy+dj+1天复活。就从 i x ix ix向 i y + d j + 1 iy + dj + 1 iy+dj+1的点连一条流量为 I N F INF INF, 费用为 q j qj qj的点。可能有人会问,难道必须要在 d j + 1 dj + 1 dj+1后复活吗?不是的,因为濒死的人会传到下一天,所以他直接复活是不会影响的。
Code
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAXL 105
#define MAXN 10005
#define MAXM 300005
#define LL long long
#define Int register int
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
typedef pair<LL, LL> Pair;
inline void read(LL &x)
{
x = 0;
LL f = 1;
char s = getchar();
while (s < '0' || s > '9')
{
if (s == '-')
f = -1;
s = getchar();
}
while (s >= '0' && s <= '9')
{
x = (x << 3) + (x << 1) + (s ^ 48);
s = getchar();
}
x *= f;
}
LL Min(LL x,LL y)
{
return x < y ? x : y;
}
struct node
{
LL to, next, flow, cost;
node(){}
node(LL TO,LL NEXT,LL FLOW,LL COST)
{
to = TO;
next = NEXT;
flow = FLOW;
cost = COST;
}
}Edge[MAXN << 1];
LL tot = -1, head[MAXN], n, m, k;
void AddEdge_Double(LL x,LL y,LL w,LL mon)
{
Edge[++ tot] = node(y, head[x], w, mon);
head[x] = tot;
Edge[++ tot] = node(x, head[y], 0, -mon);
head[y] = tot;
}
struct cmp
{
bool operator()(Pair x,Pair y)
{
if (x.first < y.first)
return 0;
if (x.first == y.first && x.second < y.second)
return 0;
return 1;
}
};
LL h[MAXN], dis[MAXN], Pred[MAXN], Preb[MAXN];
Pair Min_Max_Flow(LL s,LL t)
{
LL Flow = 0, Cost = 0;
while ( 1 )
{
memset(h, 0, sizeof h);
memset(dis, 0x3f, sizeof dis);
for (Int i = 0; i < MAXN; ++ i)
dis[s] = 0;
priority_queue<Pair, vector<Pair>, cmp> Q;
Q.push(Pair(0, s));
while (! Q.empty())
{
Pair u = Q.top();
Q.pop();
if (u.first != dis[u.second])
continue;
if (u.second == t)
break;
for (LL i = head[u.second]; ~ i; i = Edge[i].next)
{
node v = Edge[i];
LL NowCost = Edge[i].cost + h[u.second] - h[v.to];
if (v.flow > 0 && dis[v.to] > dis[u.second] + NowCost)
{
dis[v.to] = dis[u.second] + NowCost;
Q.push(Pair(dis[v.to], v.to));
Pred[v.to] = u.second;
Preb[v.to] = i;
}
}
}
if (dis[t] == INF)
break;
for (Int i = 0; i < MAXN; ++ i)
h[i] = Min(h[i] + dis[i], INF);
LL NowFlow = INF;
for (LL x = t; x != s; x = Pred[x])
NowFlow = Min(NowFlow, Edge[Preb[x]].flow);
Flow += NowFlow;
Cost += NowFlow * h[t];
for (LL x = t; x != s; x = Pred[x])
{
Edge[Preb[x]].flow -= NowFlow;
Edge[Preb[x] ^ 1].flow += NowFlow;
}
}
return Pair(Flow, Cost);
}
LL Abs(LL x)
{
if (x > 0)
return x;
return -x;
}
LL JL(LL x1,LL y1,LL x2,LL y2)
{
return Abs(x1 - x2) + Abs(y1 - y2);
}
LL Xu(LL x,LL y)
{
return (x - 1) * m + y;
}
LL a[MAXN], l[MAXN], p[MAXN], d[MAXN], q[MAXN];
int main()
{
LL T;
read( T );
LL cnt = 0;
while (T --)
{
LL Sum = 0;
tot = -1;
memset(head, -1, sizeof head);
read( n ); read( m ); read( k );
for (Int i = 1; i <= n; ++ i)
read( a[i] ), Sum += a[i];
for (Int i = 1; i <= m; ++ i)
read( l[i] ), read( p[i] );
for (Int i = 1; i <= k; ++ i)
read( d[i] ), read( q[i] );
LL s = 0, t = MAXN - 1;
for (Int i = 1; i <= n; ++ i)
AddEdge_Double(s, i, a[i], 0),
AddEdge_Double(i + n, t, a[i], 0);
for (Int j = 1; j <= m; ++ j)
{
AddEdge_Double(s, 2 * n + j, l[j], 0);
for (Int i = 1; i <= n; ++ i)
AddEdge_Double(2 * n + j, i + n, INF, p[j]);
}
for (Int i = 1; i < n; ++ i)
AddEdge_Double(i, i + 1, INF, 0);
for (Int j = 1; j <= k; ++ j)
{
for (Int i = 1; i < n; ++ i)
if (i + d[j] + 1 <= n)
AddEdge_Double(i, i + n + d[j] + 1, INF, q[j]);
}
Pair Ans = Min_Max_Flow(s, t);
if (Sum != Ans.first)
printf("Case %lld: impossible\n", ++ cnt);
else printf("Case %lld: %lld\n", ++ cnt, Ans.second);
}
return 0;
}