题目大意
你需要保证第(i)天时有第(a_i)种书。你可以在任何一天买书,买第(i)种书的代价为(c_i)。
你最多同时拥有(k)本书,如果此时再买书,则必须先扔掉已拥有的一本书。并且如果以后用到那本书则需要重新购买。
问最小的代价使满足(n)天中的所有条件都满足。(medium : n,k leq 400000)且保证所有的(c_i = 1)
(hard : n,k leq 80)
(all : a_i leq n,c_i leq 10^6)
题解
对于(medium)来说直接贪心就好。
每次需要删除的时候找出从当前位置往后最后一个出现的删除就好了。
调个(set)。注意不能用堆.(不要问我是怎么知道的)
主要问题在于(hard)。
(n,k)的范围又小了下来。
这提醒了我们已经不存在贪心的策略了。
不过我们可以从先前的贪心策略中得到启发。
如果我们只盯着一个存放书的位置上看,那么就是一个这个位子上的书不断变化的过程。
变化是为了去满足一些条件,也是因为要去满足下一个条件才变化的。
那么考虑如果将要满足的下一个条件确定了,那么这个位置在满足条件之前不会改变。
可以视作这个位置为了满足某个条件被占用了一段时间。
如果我们提前承受了所有的代价,那么下面我们就要处理收益问题。
这时候的收益即一个数在某一段时间保持不变只为下一次这个数出现的时候少一次购买。
所以我们可以建立这样的模型:
弧线边只连接上一个与其相同的数(+1)的位置和该点所在的位置。
还有要注意的一点是 : 不能让所有的流量都去满足其他条件限制
至少应该有一个流量留在主线上。
所以我们可以直接将输入的(k--)...
然后跑最小费用最大流即可.
(medium:)
#include <set>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
x=0;char ch;bool flag = false;
while(ch=getchar(),ch<'!');if(ch == '-')ch=getchar(),flag = true;
while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
#define rg register int
#define rep(i,a,b) for(rg i=(a);i<=(b);++i)
#define per(i,a,b) for(rg i=(a);i>=(b);--i)
const int maxn = 400010;
int a[maxn],nx[maxn],ws[maxn];
set<int>s;
bool inq[maxn];int cnt = 0;
int main(){
int n,k;read(n);read(k);
memset(ws,0x3f,sizeof ws);
rep(i,1,n) read(a[i]);
per(i,n,1){
nx[i] = ws[a[i]];
ws[a[i]] = i;
}
int ans = 0;
rep(i,1,n){
if(s.count(i)){
s.erase(i);
s.insert(nx[i]);
continue;
}
if(s.size() < k){
++ ans;
s.insert(nx[i]);
}else{
s.erase(--s.end());
s.insert(nx[i]);
++ ans;
}
}
printf("%d
",ans);
return 0;
}
(hard:)
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
x=0;char ch;bool flag = false;
while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
#define rg register int
#define rep(i,a,b) for(rg i=(a);i<=(b);++i)
#define per(i,a,b) for(rg i=(a);i>=(b);--i)
const int maxn = 128;
const int maxnode = maxn;
const int maxedge = maxn*maxn;
const int inf = 0x3f3f3f3f;
struct Edge{
int to,next,cap,cost;
}G[maxedge<<1];
int head[maxnode],cnt = 1;
void add(int u,int v,int c,int d){
G[++cnt].to = v;
G[cnt].next = head[u];
head[u] = cnt;
G[cnt].cap = c;
G[cnt].cost = d;
}
inline void insert(int u,int v,int c,int d){
add(u,v,c,d);add(v,u,0,-d);
}
#define v G[i].to
int dis[maxn],flow[maxn],p[maxn];
bool inq[maxn];int S,T;
int ans;
inline bool spfa(){
queue<int>q;
memset(dis,0x3f,sizeof dis);
dis[S] = 0;flow[S] = inf;
inq[S] = true;
q.push(S);
while(!q.empty()){
int u = q.front();q.pop();
for(rg i = head[u];i;i=G[i].next){
if(dis[v] > dis[u] + G[i].cost && G[i].cap){
dis[v] = dis[u] + G[i].cost;
flow[v] = min(flow[u],G[i].cap);
p[v] = i;
if(!inq[v]){
q.push(v);
inq[v] = true;
}
}
}inq[u] = false;
}if(dis[T] == dis[0]) return false;
ans += dis[T]*flow[T];
for(rg u=T;u != S;u = G[p[u]^1].to)
G[p[u]].cap -= flow[T],G[p[u]^1].cap += flow[T];
return true;
}
#undef v
int a[maxn],c[maxn],ws[maxn];
int main(){
int n,k;read(n);read(k);
-- k;
rep(i,1,n) read(a[i]);
rep(i,1,n) read(c[i]);
S = n + 1;T = n+2;
insert(S,1,k,0);
insert(n,T,k,0);
rep(i,1,n-1) insert(i,i+1,k,0);
rep(i,1,n){
ans += c[a[i]];
if(ws[a[i]] != 0){
if(ws[a[i]]+1 == i) ans -= c[a[i]];
else insert(ws[a[i]]+1,i,1,-c[a[i]]);
}
ws[a[i]] = i;
}
while(spfa());
printf("%d
",ans);
return 0;
}