Problem
(n) 个关卡,对每个关卡,你可以花 (a_i) 代价得到一颗星,也可以花 (b_i) 代价得到两颗星,也可以不玩。问获得 (w) 颗星最少需要多少时间。
Sol
反悔贪心好题。
时隔半年终于落实了
我们考虑如何如何多拿一颗星星:
-
0 -> 1(在一个不拿星星的关卡里拿一颗星星)
-
1 -> 2
发现需要反悔才可以。
于是
-
2 -> 1 ,0 -> 2
-
1 -> 0 , 0 -> 2
开 5 个堆记录就行了,细节见代码注释。
Code
#define in read()
#define fi first
#define se second
#define pair<int,int> pii
typedef long long ll;
int read(){
int x = 0,sgn = 1;char ch = getchar();
for(;!isdigit(ch);ch = getchar()) if(ch == '-') sgn = -1;
for(;isdigit(ch);ch = getchar()) x = (x<<1)+(x<<3)+(ch^48);
return x*sgn;
}
priority_queue<pii,vector<pii>,greater<pii> > q[5];
//小根堆
const int N = 3e5+10;
const int inf = 0x7fffffff;
int vis[N],a[N],b[N],statu[5] = {0,1,2,1,0};
ll t[5],th[5]; //血泪的教训,一定要开 long long
int n,m;
ll ans;
/*
q[0] : 0 -> 1
q[1] : 1 -> 2
q[2] : 2 -> 1
q[3] : 1 -> 0
q[4] : 0 -> 2
*/
void add0(int x){
q[0].push(pii(a[x],x));
q[4].push(pii(b[x],x));
vis[x] = 0;
}
void add1(int x){
q[1].push(pii(b[x]-a[x],x));
q[3].push(pii(-a[x],x));
vis[x] = 1;
}
void add2(int x){
q[2].push(pii(a[x]-b[x],x));
vis[x] = 2;
}
int main (){
n = in,m = in;
for(int i = 0;i < 5;i++) q[i].push(pii(inf,0));
for(int i = 1;i <= n;i++) a[i] = in,b[i] = in,add0(i);
for(int i = 1;i <= m;i++){
for(int j = 0;j < 5;j++) {
t[j] = q[j].top().fi,th[j] = q[j].top().se;
while(t[j] < inf && th[j] && statu[j] != vis[th[j]]){
q[j].pop(); t[j] = q[j].top().fi,th[j] = q[j].top().se;
}
}
ll minn = inf;
minn = min(t[0],min(t[1],min(t[2]+t[4],t[3]+t[4])));
if(minn == t[0]){
q[0].pop(); add1(th[0]);
}else if(minn == t[1]){
q[1].pop(); add2(th[1]);
}else if(minn == t[2] + t[4]){
q[2].pop(); q[4].pop(); add1(th[2]); add2(th[4]);
}else{
q[3].pop(); q[4].pop(); add0(th[3]); add2(th[4]);
}ans += minn;
}
printf("%lld
",ans);
for(int i = 1;i <= n;i++) printf("%d",vis[i]);
puts("");
return 0;
}