后悔了
CF436E Cardboard Box
法一:反悔贪心
考虑从一个最优的状态去推向另一个最优的状态,我们希望每次的星星都增加 \(1\) 颗。
则有普通操作:
1.花费 \(a_i\) 获得一颗星星。
2.花费 \(b_i-a_i\) 再获得一颗星星。
反悔操作:
1.花费 \(b_j-a_i\) 让一个关卡的一颗星星退回,让另一个关卡获得两颗星星。
2.花费 \(b_i-a_i+b_j\) 让一个关卡退回一颗星星的形态,选择另一个关卡获得两颗星星。
这些东西可以拿一堆堆维护。
考虑为什么只有这些操作是对的,是因为我们借助了每次都是当前最优局面这个性质。
其它的反悔操作不需要是因为是多余的,反证就是起了作用就证明当前局面不是最优的,所以若是最优的就不起作用。
点击查看代码
#include <bits/stdc++.h>
#define int long long
#define rg register
#define pc putchar
#define gc getchar
#define pf printf
#define space pc(' ')
#define enter pc('\n')
#define me(x,y) memset(x,y,sizeof(x))
#define pb push_back
#define FOR(i,k,t,p) for(rg int i(k) ; i <= t ; i += p)
#define ROF(i,k,t,p) for(rg int i(k) ; i >= t ; i -= p)
using namespace std ;
bool s_gnd ;
inline void read(){}
template<typename T,typename ...T_>
inline void read(T &x,T_&...p)
{
x = 0 ;rg int f(0) ; rg char c(gc()) ;
while(!isdigit(c)) f |= (c=='-'),c = gc() ;
while(isdigit(c)) x = (x<<1)+(x<<3)+(c^48),c = gc() ;
x = (f?-x:x) ;
read(p...);
}
int stk[30],tp ;
inline void print(){}
template<typename T,typename ...T_>
inline void print(T x,T_...p)
{
if(x < 0) pc('-'),x = -x ;
do stk[++tp] = x%10,x /= 10 ; while(x) ;
while(tp) pc(stk[tp--]^48) ; print(p...) ;
}
bool S_GND ;
const int INF = 1e18 ;
const int N = 1e6+6 ;
int sum ;
int n,w,op1,op2 = 1,op3 = 1,op4,op5 = 2 ;
int a[N],b[N],ans[N] ;
struct Node
{
int id,val ;
bool operator < (const Node &A) const
{
return val > A.val ;
}
} ;
priority_queue<Node>q1,q2,q3,q4,q5 ;
signed main()
{
//cerr<<(double)(&s_gnd-&S_GND)/1024.0/1024.0 ;
// freopen(".in","r",stdin) ;
// freopen(".out","w",stdout) ;
read(n,w) ;
FOR(i,1,n,1) read(a[i],b[i]),q1.push({i,a[i]}),q4.push({i,b[i]}) ;
FOR(i,1,w,1)
{
int mi = INF,op = 1,pos = 0,ppos = 0 ;
while(!q1.empty() && ans[q1.top().id] != op1) q1.pop() ;
while(!q2.empty() && ans[q2.top().id] != op2) q2.pop() ;
while(!q3.empty() && ans[q3.top().id] != op3) q3.pop() ;
while(!q4.empty() && ans[q4.top().id] != op4) q4.pop() ;
while(!q5.empty() && ans[q5.top().id] != op5) q5.pop() ;
if(!q1.empty() && q1.top().val < mi)
mi = q1.top().val,pos = q1.top().id,op = 1 ;
if(!q2.empty() && q2.top().val < mi)
mi = q2.top().val,pos = q2.top().id,op = 2 ;
if(!q3.empty() && !q4.empty() && q3.top().val+q4.top().val < mi)
mi = q3.top().val+q4.top().val,pos = q3.top().id,ppos = q4.top().id,op = 3 ;
if(!q4.empty() && !q5.empty() && q4.top().val+q5.top().val < mi)
mi = q4.top().val+q5.top().val,pos = q5.top().id,ppos = q4.top().id,op = 4 ;
sum += mi ; //print(mi),enter ;
if(op == 1) ans[pos] = 1,q2.push({pos,b[pos]-a[pos]}),q3.push({pos,-a[pos]});
if(op == 2) ans[pos] = 2,q5.push({pos,a[pos]-b[pos]});
if(op == 3) ans[pos] = 0,ans[ppos] = 2,q1.push({pos,a[pos]}),q4.push({pos,b[pos]}),q5.push({ppos,a[ppos]-b[ppos]}) ;
if(op == 4) ans[pos] = 1,ans[ppos] = 2,q2.push({pos,b[pos]-a[pos]}),q3.push({pos,-a[pos]}),q5.push({ppos,a[ppos]-b[ppos]}) ;
}
print(sum),enter ;
FOR(i,1,n,1) print(ans[i]) ;
return 0 ;
}
法二:线段树
和反悔贪心有些类似,都用了最优局面的性质,但又有点不同。
现将数组按 \(b_i\) 排序。
考虑最优局面的一个性质,若最后第 \(L\) 个是最后一个选择两颗星的,则前 \(L\) 个都至少有一颗星,后面的可选可不选。
考虑反证,就是若第 \(L\) 个选了两颗星,前面有没选星的,则可以将没选的改为两颗星,这个改为不选星会更优。
所以前面的要么选了一颗星,要么选了两颗星。
所以我们枚举第 \(L\) 个是最后一个选两颗星的,然后将前面的第二颗星和后面的全部丢进线段树里面区间求和就好了。
CF730I Olympiad in Programming and Sports
但这题和上面的一题不一样,两个限制不太好搞。
考虑先满足一个限制,然后一次去满足另一个限制。
考虑先按 \(a\) 排序,然后取前 \(p\) 个作为最初的初始局面,依次将后面 \(s\) 填上,反悔机制比较简单不说了。
CF865D Buy Low Sell High
比较简单的一道题,操作为将卖了的退回再买,或者直接买,注意一天只能买一次就好了。

浙公网安备 33010602011771号