首先, 等死是没有必要的. 因为我们限制的是吃饭,睡觉天数的下界, 而所有权值非负. 不妨假设每天吃饭, 获得
听了正解感到很迷茫......有两种流派: - 转成线性规划问题, 把式子整一整, 根据流量平衡连边. - 看作一种新的模型, 通过感性认知建模......
后来找到了另一种个人觉得可以接受的流派: - 转化为已知模型, 开开心心地建模.
第一种方法我在考场上尝试过, 然后失败了......主要是因为引入了前缀和作为变量, 但是列出来的式子不能保证每个变量在等号两边至多各出现一次.
经过一番努力, 后来用这种方法做出来了, 但是一点也不优美......一开始建出的图还有负圈 (只会连续最短增广路算法 TAT)......换了一种操作方式, 终于建出了和题解一样的图. 所以问题来了, 我怎么预先知道图有没有负圈......
网络流与线性规划 |
线性规划问题, 如果每个变量至多在等号两边各出现一次, 就可以用费用流跑. |
每个等式一个点. |
如果变量 |
常量同理. 要让代表常量的边流满, 所以跑最小/大费用最大流. |
本题建出来的图是这样的: - 除源, 汇外, 共有
是不是很像区间k覆盖问题呢?
区间k覆盖 有 |
- 设 (离散化后) 涉及到的值域为 |
每个流量对应一组互不相交的区间, 因此每个点不会被覆盖多于 |
每个合法方案可以被分解成不超过 |
注意, 这里是半开半闭区间. 这样才能保证 |
最大费用可以在满流的时候取到, 所以可以跑最大费用最大流. |
本题就是区间k覆盖问题. 我们用右端点
更准确地说: 我们有
令
新颖之处在于, 本问题中, 每个点被覆盖的次数有下界.
这个网络的最大流为
typedef long long ll;
const int N = 1001;
const ll inf = 1LL<<60;
struct MinCostFlow
{
const static int V = N + 3;
bool inq[V];
int s, t, n, p[V], adj[V];
ll d[V];
queue<int> Q;
struct Edge
{
int to, c, w, next;
} E[4*N + 2];
void add(int u, int v, int c, int w)
{
static int ptr = 2;
E[ptr] = (Edge){v, c, w, adj[u]};
adj[u] = ptr++;
E[ptr] = (Edge){u, 0, -w, adj[v]};
adj[v] = ptr++;
}
bool spfa(ll& init, ll& flow, ll& cost)
{
fill_n(d, n, -inf);
Q.push(s);
d[s] = 0;
while (!Q.empty())
{
int u = Q.front(); Q.pop();
inq[u] = false;
for (int i = adj[u]; i; i = E[i].next)
{
int v = E[i].to;
if (E[i].c && d[v] < d[u] + E[i].w)
{
p[v] = i;
d[v] = d[u] + E[i].w;
if (!inq[v])
inq[v] = true, Q.push(v);
}
}
}
if (d[t] == -inf) return false;
ll f = init;
for (int v = t; v != s; v = E[p[v]^1].to)
f = min(f, (ll)E[p[v]].c);
for (int v = t; v != s; v = E[p[v]^1].to)
E[p[v]].c -= f, E[p[v]^1].c += f;
init -= f;
flow += f;
cost += d[t] * f;
return true;
}
ll main(int _s, int _t, int _n, ll _i)
{
s = _s, t = _t, n = _n;
ll c = 0, f = 0, i = _i;
while (i && spfa(i, f, c)) ;
return c;
}
} F;
int s[N], e[N];
int main()
{
int n, k, S, E;
ll sum = 0;
scanf("%d%d%d%d", &n, &k, &S, &E);
rep (i, 1, n+1) scanf("%d", s+i), sum += s[i];
rep (i, 1, n+1) scanf("%d", e+i);
rep (i, 1, k+1) F.add(0, min(i, n-k+1), 1, e[i]-s[i]);
rep (i, k+1, n+1) F.add(i-k, min(i, n-k+1), 1, e[i]-s[i]);
rep (i, 0, n-k+1) F.add(i, i+1, k-S-E, 0);
printf("%lld\n", sum + F.main(0, n-k+1, n-k+2, k-S));
return 0;
}