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=1,ans,fl[N],dis[N],pre[N],n,k,a[N],c[N],now[N]; bool vis[N]; inline int rd(){ int x=0;char c=getchar();bool f=0; while(!isdigit(c)){if(c=='-')f=1;c=getchar();} while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);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=0;e[tot].f=-f; } inline bool spfa(int s,int t){ memset(dis,0x3f,sizeof(dis)); q.push(s);dis[s]=0;fl[s]=inf; while(!q.empty()){ int u=q.front();q.pop();vis[u]=0; 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]=1;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^1].l+=fl[t]; x=e[i^1].to; } ans+=fl[t]*dis[t]; } int main(){ n=rd();k=rd(); for(int i=1;i<=n;++i)a[i]=rd(); for(int i=1;i<=n;++i)c[i]=rd(); for(int i=1;i<=n;++i){ if(i!=n)add(i,i+1,k-1,0); add(0,i,1,c[a[i]]); add(i,i+n,1,0);add(i+n,2*n+1,1,0);now[a[i]]=i; if(now[a[i+1]]){ add(i,now[a[i+1]]+n,1,-c[a[i+1]]); } } while(spfa(0,2*n+1))calc(0,2*n+1); cout<<ans; return 0; }