题目跳转:P10083 [GDKOI2024 提高组] 不休陀螺
简要题意
有 n n n 张牌,每张牌有先代价 a i a_i ai,后贡献 b i b_i bi。
对于每一个区间,每次随机抽取一张牌,如果当前费用小于此牌代价,则失败。如果一直随机抽取知道区间中无牌,则区间可取。
求 n n n 张牌中可取区间的个数 (区间是连续的)。
思维思路
判断一个区间可取
在一个区间内:
-
如果代价的总和不及贡献总和,即 ∑ i = l r \sum^r_{i=l} ∑i=lr ( b i − a i ) ≥ 0 (b_i-a_i)\ge 0 (bi−ai)≥0
-
我们先定义 a i > b i a_i>b_i ai>bi 的牌为亏牌,则 a i ≤ b i a_i\le b_i ai≤bi 为盈牌。我们想要最小化 E E E,那么我们要让最大化 E E E 的消耗。
有两种方式:
-
把所有亏牌放在前面,则刷完亏牌 E E E 会减少 ∑ i = l r \sum^r_{i=l} ∑i=lr ( a i − b i ) [ a i > b i ] (a_i-b_i)[a_i>b_i] (ai−bi)[ai>bi]。
可以简化为 ∑ i = l r \sum^r_{i=l} ∑i=lr min ( 0 , a i − b i ) \min(0,a_i-b_i) min(0,ai−bi)。
-
在刷完亏牌后选择一张盈牌加上其代价( a i a_i ai)或亏牌减去其贡献( b i b_i bi),选择最大值并减去。
因为盈是 a i ≤ b i a_i\le b_i ai≤bi,亏是 a i > b i a_i>b_i ai>bi。容易看出是最大化 min ( a i , b i ) \min(a_i,b_i) min(ai,bi)。
总结为 max i = l r \max^r_{i=l} maxi=lr min ( a i , b i ) \min(a_i, b_i) min(ai,bi)。
最后 E E E 大于等于 0 0 0 则可取。
是 E − ∑ i = l r E-\sum^r_{i=l} E−∑i=lr min ( 0 , a i − b i ) ≥ max i = l r \min(0,a_i-b_i) \ge \max^r_{i=l} min(0,ai−bi)≥maxi=lr min ( a i , b i ) ) \min(a_i, b_i)) min(ai,bi))。
满足以上两个条件的区间可取。
实现思路
O ( n 2 ) : O(n^2): O(n2): 直接暴力判断每个区间。
O ( n log n ) : O(n \log n): O(nlogn):
对于第一个不等式,令 c i = ∑ j = 1 i ( b j − a j ) c_i =\sum_{j=1}^i(b_j-a_j) ci=∑j=1i(bj−aj), 那么只需要满足 c r ≥ c l − 1 c_ r \ge c_{l-1} cr≥cl−1。
对于第二的不等式,发现其在右端点单调不降,可以使用双指针解决。
具体地,每次将右端点移动最远,将前缀和离散化后使用值域树状数组实现。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
const int maxm = __lg(maxn) + 1;
int a[maxn], b[maxn], f[maxm][maxn], n;
long long c[maxn], d[maxn], e;
int qmax(int l, int r) {
return l > r ? 0 : max(f[__lg(r - l + 1)][l], f[__lg(r - l + 1)][r - (1 << __lg(r - l + 1)) + 1]);
}
struct node {
int sum[maxn];
int lowbit (int x) {
return x & -x;
}
void add (int p, int val) {
for(int i = p; i <= n + 1; i += lowbit(i)) sum[i] += val;
}
int query (int p) {
int res = 0;
for(int i = p; i > 0; i -= lowbit(i)) res += sum[i];
return res;
}
} bit;
int main() {
cin >> n >> e;
for (int i = 1; i <= n; ++i) cin >> a[i];
for (int i = 1; i <= n; ++i)
cin >> b[i], c[i] = c[i - 1] - (a[i] - b[i]), d[i] = c[i];
sort(d + 1, d + n + 1);
int len = unique(d, d + n + 1) - d;
for (int i = 0; i <= n; ++i)
c[i] = lower_bound(d, d + len, c[i]) - d + 1;
for (int i = 1; i <= n; ++i) f[0][i] = min(a[i], b[i]);
for (int i = 1; 1 << i <= n; ++i)
for (int j = 1; j + (1 << i) - 1 <= n; ++j)
f[i][j] = max(f[i - 1][j], f[i - 1][j + (1 << i - 1)]);
long long ans = 0;
for (int l = 1, r = 1; l <= n; ++l) {
while (r <= n && qmax(l, r) <= e - max(a[r] - b[r], 0))
bit.add(c[r], 1), e -= max(a[r] - b[r], 0), r++;
ans += bit.query(n + 1) - bit.query(c[l - 1] - 1);
bit.add(c[l], -1);
e += max(a[l] - b[l], 0);
}
cout << ans << '\n';
}