【讲题用】超时空彩叶姬 题解
Description
一共 \(n\) 周。
初始:\(cash=0\),\(coin=0\)
每周彩叶:
-
先获得 \(b_i\) 元。
\(cash \leftarrow cash+b_i\) -
然后把其中任意一部分兑换为福币(可以全换或不换),\(1\) 元可以兑换 \(a_i\) 福币。
\(cash \leftarrow cash - c_i\),\(coin \leftarrow coin + c_i a_i\) -
再消费固定的 \(x\) 福币。
\(coin \leftarrow coin - x\)
之前没用完的日元和福币可以接着用。
求最大可能 \(x\)。
Solution
\(x\) 越大越难满足要求,答案具有单调性,于是二分。
二分关键在于两点:值域和 check 函数。
值域:\([1,a_1 b_1]\)。
check:贪心。
-
若今后没有 \(a_i\) 更大的周,那就把 \(cash\) 全换成 \(coin\)。
-
若今后有 \(a_i\) 更大的周,设离当今最近的是第 \(j\) 周,那么尽可能换够直至第 \(j-1\) 周所需的 \(coin\)。
接下来问题是如何得到 \(j\) 的值。醋:单调栈。
Code
#include <bits/stdc++.h>
typedef long long i64;
using namespace std;
const i64 INF = LLONG_MAX;
const int MAXN = 2e6;
i64 ceil_div(i64 a, i64 b) {
return (a + b - 1) / b;
}
int n;
i64 a[MAXN + 1], b[MAXN + 1], p[MAXN + 1]; // p[i] 下一个更划算的周
bool isLegal(i64 x) {
i64 cash = 0, coin = 0;
for (int i = 1; i <= n; ++i) {
cash += b[i];
if (p[i] == 0) {
coin += cash * a[i];
cash = 0;
}
else {
i64 need = (p[i] - i) * x;
if (coin < need) {
i64 lack = need - coin; // 不要忘记已有的币
i64 buy = min(ceil_div(lack, a[i]), cash);
coin += buy * a[i];
cash -= buy;
}
}
coin -= x;
if (coin < 0)
return false;
}
return true;
}
void solve() {
cin >> n;
for (int i = 1; i <= n; ++i)
cin >> a[i];
for (int i = 1; i <= n; ++i)
cin >> b[i];
stack<int> st;
for (int i = n; i >= 1; --i) { // 从后往前跑单调栈
while (!st.empty() && a[i] >= a[st.top()])
st.pop();
p[i] = st.empty() ? 0 : st.top();
st.push(i);
}
i64 l = 0, r = a[1] * b[1];
while (l < r) {
i64 mid = (l + r + 1) >> 1;
if (isLegal(mid))
l = mid;
else
r = mid - 1;
}
cout << l << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--)
solve();
return 0;
}

浙公网安备 33010602011771号