【YBT2022寒假Day6 B】【luogu CF802C】大图书馆 / Heidi and Library (hard)(网络流)
大图书馆 / Heidi and Library (hard)
题目链接:YBT2022寒假Day6 B / luogu CF802C
题目大意
你有一个容量为 k 的桶,然后每天有需求要一个编号的东西,这天结束时会还回来。
然后你可以花一定的钱买入一个编号的东西,或者把东西扔掉,买的东西不能超过容量数,然后问你最小花费。
思路
考虑网络流建图。
首先是每天要买,源点连向点流量 \(1\) 费用为买的花费,然后是提供,每天都要提供,所以点想汇点连流量为 \(1\)费用为 \(0\)。
那你会一个提供多个啊,你网络流无法实现啊。
那我们考虑这样,我们肯定每个都买,然后我们弄一个类似卖的操作。即如果之前有这个,那我们就可以走一个卖的边来退钱。
那我们考虑拆点,前面的就源点留到拆出的第一个点,第一个点连流量 \(1\) 费用 \(0\) 点到第二个点,再连到汇点。
然后保留的操作我们就每个点拆出的第一个点顺着连过去,流量是 \(k-1\) 费用是 \(0\)。
(这里是 \(k-1\) 是因为你当日要买入给今天用的,所以就一定要占一个位置,而且这样的话我们要保留这个种类的话保留到它前一天就可以结束了)
然后就是卖出了,因为上面这句话,所以我们是从这个点的上一个点拆出来的第一个连向这个种类上一次出现的点拆出来的第二个,费用是这次的费用的相反数,流量是 \(1\)。
然后跑费用流就可以得出结果了。
代码
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
struct node {
int x, to, nxt, cst, op;
}e[1000001];
int n, k, c[1001], tot, S, T, lee[2501];
int lst[1001], a[1001], le[2501], KK;
void add(int x, int y, int z, int va) {
e[++KK] = (node){z, y, le[x], va, KK + 1}; le[x] = KK;
e[++KK] = (node){0, x, le[y], -va, KK - 1}; le[y] = KK;
}
queue <int> q;
int dis[2501], deg[2501];
bool in[2501];
bool SPFA() {
memset(dis, 0x7f, sizeof(dis));
memset(deg, 0, sizeof(deg));
memcpy(lee, le, sizeof(lee));
q.push(S); dis[S] = 0; in[S] = 1; deg[S] = 1;
while (!q.empty()) {
int now = q.front(); q.pop();
for (int i = le[now]; i; i = e[i].nxt)
if (e[i].x && dis[e[i].to] > dis[now] + e[i].cst) {
dis[e[i].to] = dis[now] + e[i].cst;
deg[e[i].to] = deg[now] + 1;
if (!in[e[i].to]) {
in[e[i].to] = 1; q.push(e[i].to);
}
}
in[now] = 0;
}
return dis[T] != dis[0];
}
int dfs(int now, int sum) {
if (now == T) return sum;
int go = 0; in[now] = 1;
for (int i = le[now]; i; i = e[i].nxt)
if (e[i].x && dis[e[i].to] == dis[now] + e[i].cst && deg[e[i].to] == deg[now] + 1 && !in[e[i].to]) {
int this_go = dfs(e[i].to, min(e[i].x, sum - go));
if (this_go) {
e[i].x -= this_go; e[e[i].op].x += this_go;
go += this_go; if (go == sum) {in[now] = 0; return go;}
}
}
if (go != sum) dis[now] = -1;
in[now] = 0; return go;
}
pair <int, int> dinic() {
pair <int, int> re = make_pair(0, 0);
while (SPFA()) {
int x = dfs(S, INF);
re.first += x; re.second += dis[T] * x;
}
return re;
}
int main() {
// freopen("bibliotheca.in", "r", stdin);
// freopen("bibliotheca.out", "w", stdout);
scanf("%d %d", &n, &k);
tot = 2 * n; S = ++tot; T = ++tot;
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
for (int i = 1; i <= n; i++) {
scanf("%d", &c[i]);
}
for (int i = 1; i <= n; i++) {
add(S, i, 1, c[a[i]]); add(i + n, T, 1, 0); add(i, i + n, 1, 0);
if (lst[a[i]]) add(i - 1, lst[a[i]] + n, 1, -c[a[i]]);
lst[a[i]] = i;
if (i != n) add(i, i + 1, k - 1, 0);
}
pair <int, int> re = dinic();
if (re.first != n) return -1;
else printf("%d", re.second);
return 0;
}