CF802C Heidi and Library (hard)

时间:2023-03-09 01:31:28
CF802C Heidi and Library (hard)

题目描述

你有一个容量为k的空书架,现在共有n个请求,每个请求给定一本书ai,如果你的书架里没有这本书,你就必须以ci的价格购买这本书放入书架。当然,你可以在任何时候丢掉书架里的某本书。请求出完成这n个请求所需要的最少价钱。

题解

对于每个请求,我们可以强制让他必须买,然后再去考虑如何扣掉之前的贡献。

对于每一次购买,我们可以作如下两种策划。

1、刚买完就扔掉。2、在下一次购买之前把这本书卖掉(这里的卖掉相当于是把上一次买的代价减去了)。

按照这样的策略,我们的每一本买来的书最终都会被弄掉 。

这样的话,我们给每一次购买开一个垃圾桶,表示这本书经过若干轮之后必须回到垃圾桶里。

设源点为S,汇点为T。

从源点向每次询问连边权为1,费用为w的边,表示每次询问一定要买。

每次询问向每次的垃圾桶连边权为1,费用为0,表示我可以刚买完就扔掉。

每次询问向下一个询问连边权为k-1,费用为0的边,表示在下一次购买前我最多可以有k-1本书。

对于一次询问,如果下一次询问的书之前出现过,则一条从当前询问到那一次的垃圾桶连一条边权为1,费用-w的边,代表把书卖掉的情况。

费用流即可。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define N 170
#define inf 1e9
using namespace std;
queue<int>q;
int head[N],tot=,ans,fl[N],dis[N],pre[N],n,k,a[N],c[N],now[N];
bool vis[N];
inline int rd(){
int x=;char c=getchar();bool f=;
while(!isdigit(c)){if(c=='-')f=;c=getchar();}
while(isdigit(c)){x=(x<<)+(x<<)+(c^);c=getchar();}
return f?-x:x;
}
struct edge{
int n,to,l,f;
}e[N*N];
inline void add(int u,int v,int l,int f){
e[++tot].n=head[u];e[tot].to=v;head[u]=tot;e[tot].l=l;e[tot].f=f;
e[++tot].n=head[v];e[tot].to=u;head[v]=tot;e[tot].l=;e[tot].f=-f;
}
inline bool spfa(int s,int t){
memset(dis,0x3f,sizeof(dis));
q.push(s);dis[s]=;fl[s]=inf;
while(!q.empty()){
int u=q.front();q.pop();vis[u]=;
for(int i=head[u];i;i=e[i].n){
int v=e[i].to;
if(dis[v]>dis[u]+e[i].f&&e[i].l){
dis[v]=dis[u]+e[i].f;//cout<<u<<" "<<v<<" "<<dis[u]<<" "<<dis[v]<<endl;
pre[v]=i;fl[v]=min(fl[u],e[i].l);
if(!vis[v]){vis[v]=;q.push(v);}
}
}
}
return dis[t]!=0x3f3f3f3f;
}
void calc(int s,int t){
int x=t;
while(x!=s){
int i=pre[x];
e[i].l-=fl[t];e[i^].l+=fl[t];
x=e[i^].to;
}
ans+=fl[t]*dis[t];
}
int main(){
n=rd();k=rd();
for(int i=;i<=n;++i)a[i]=rd();
for(int i=;i<=n;++i)c[i]=rd();
for(int i=;i<=n;++i){
if(i!=n)add(i,i+,k-,);
add(,i,,c[a[i]]);
add(i,i+n,,);add(i+n,*n+,,);now[a[i]]=i;
if(now[a[i+]]){
add(i,now[a[i+]]+n,,-c[a[i+]]);
}
}
while(spfa(,*n+))calc(,*n+);
cout<<ans;
return ;
}