CF802C Heidi and Library (hard)
自己做出的第一道黑题虽然实际才2600不难
传送门
题意
(搬自洛谷翻译
你有一个容量为\(k\)的空书架,现在共有\(n\)个请求,每个请求给定一本书\(a_i\), 如果你的书架里没有这本书,你就必须以\(c_{a_i}\)的价格购买这本书放入书架。当然,你可以在任何时候丢掉书架里的某本书。请求出完成这\(n\)个请求所需要的最少价钱。
题解
这是一个只需要n+2个点的简单做法
如何想到
首先,容易想到的是:如果不能卖出再买入,对于一个颜色 \(x\),我们在第一次出现买入,最后一次卖出。
上图, 数轴上每个点表示一天,每天的书都可以直接流向下一天,容量为 \(k\),费用为 \(0\),表示放在书架。
问题是,如何考虑卖出后再买入的操作呢?
仔细想,对于相邻两个 \(x\) 之间的一段 (如上图红色部分), 这本书要么一直在书架,要么一直不在(如果前部分在后半部分不在还不如直接在上一个x那里卖掉)。 如果不在书架的话,就不会占用书架的容量,但是在下一个 \(x\) 就一定要重新买。所以我们可以直接用一条额外的边连接相邻的两个 \(x\), 费用为 \(c_x\) , 容量为 \(1\)。
为什么是对的
这样做是对的吗?我们发现:如果这样做不是错的话那它一定是对的(。我们考虑到错的情况有两种: 一个流流过了它不该流的边或者不该流入的汇点。
1.我们称相邻两个 \(x\) 之间的边为购买边,理想状态下 \(x\) 只会走 \(x\) 的购买边,但实际上它可能走到 \(y\) 的购买边,由于购买边流量为1 ,此时 \(y\) 一定走的是书架边, 那这种情况等价于 \(y\) 走购买边而 \(x\) 走书架边。 所以, 一条流只要从 \(x\) 第一次出现的点流入最后出现的点, 那么中途无论经过什么边都是合法的
2.假如 \(x\) 流入了 \(y\) 的汇点呢?事实上由于第一点,下面两种情况可以看成等价的
而且它情况实际上都可以转化成上述情况的。
实现
有一个小细节,如果你完全按照上述建图,会有一个问题: 如果一个点刚买就卖了,他是不会走书架边的,但实际上它当天需要占用一个书架位置。那我们换一种思路, 我们认为这本书是前一天下班的时候买的, 把入边连到上一天即可。
int main()
{
memset(head,-1,sizeof(head)); num_edge=-1;
N=read(), K=read();
for(int i=1; i<=N; i++) A[i]=read();
for(int i=1; i<=N; i++) C[i]=read();
s=N+1, t=N+2;
for(int i=1; i<N; i++) addEdge(i, i+1, K, 0);
for(int i=1; i<=N; i++){
if(!VIS[A[i]]) {
if(i == 1) addEdge(N+1, i, 1, C[A[i]]);
else addEdge(N+1, i-1, 1, C[A[i]]);
}else{
addEdge(VIS[A[i]], i-1, 1, C[A[i]]);
}
VIS[A[i]] = i;
}
for(int i=1; i<=N; i++) if(VIS[i]) addEdge(VIS[i], N+2, 1, 0);
MCMF();
printf("%d\n", mincost);
return 0;
}