The content below was generated entirely by machine translation. Please verify its accuracy. If anything is unclear, consult the Chinese source version.

F. Yet Another Problem About Pairs Satisfying an Inequality

Approach (from the Official Solution)

Observe the inequality ai<i<aj<ja_i < i < a_j < j in the problem. We can see that, for any element in the array, if it does not satisfy ax<xa_x < x, then it can never form a valid pair with any element. Therefore, we can directly skip elements that do not satisfy ax<xa_x < x.

We can split this inequality into three parts: ai<ia_i < i, i<aji < a_j, and aj<ja_j < j.

For all elements satisfying ax<xa_x < x, the first and last inequalities are already satisfied. We only need to find elements satisfying i<aji < a_j to form a valid pair.

Let bb be the array obtained by removing from aa the elements that do not satisfy ax<xa_x < x (this sounds a little strange, but the index of every element in bb is the same as it was in aa).

For example, suppose an element in the array has two properties, value and index, and we denote it as (val,id)(val, id). If array aa is:

a=(1,1) (1,2) (2,3) (3,4) (8,5) (2,6) (1,7) (4,8)a = (1, 1) \ (1, 2)\ (2, 3)\ (3, 4)\ (8, 5)\ (2, 6)\ (1, 7)\ (4, 8)\\

then removing all elements with val>=idval >= id gives bb:

b=(1,2) (2,3) (3,4) (2,6) (1,7) (4,8)b = (1, 2)\ (2, 3)\ (3, 4)\ (2, 6)\ (1, 7)\ (4, 8)\\

Then we can solve the problem by finding all valid ii for every bjb_j.

It is not difficult to see that ii increases monotonically, so binary search can be used to find the largest ii smaller than bjb_j. Every element in bb whose idid is smaller than ii (as well as ii itself) can form a valid pair with aja_j.

In addition to binary search, we can use a Fenwick tree to find the number of elements in bb whose idid is smaller than a particular value.

Specifically, we can use a Fenwick tree to maintain a prefix-sum array and then traverse the elements in bb, performing upd(id) each time. This makes the value found when querying every number greater than or equal to idid increase by 11.

Thus, querying bj1b_j - 1 in the Fenwick tree returns all ii smaller than bjb_j.

Of course, a difference-array method can also produce the same prefix-sum array as the Fenwick tree. This problem does not require any further updates after obtaining the prefix-sum array, so a difference array can solve the problem in O(n)\operatorname{O}(n) time.

Code

Complexity: O(nlogn)\operatorname{O}(n \log n)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <bits/stdc++.h>
using namespace std;
#define ll long long
// author: ttzytt (ttzytt.com)
// ref: https://codeforces.com/blog/entry/104786
int main() {
int t;
cin >> t;
while (t--) {
int n;
cin>>n;
int a[n + 1];
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
ll ans = 0;
vector<int> valid; // The array b described above, but storing only indices,
// because we only need to find the largest index j smaller than b_i.
for (int i = 1; i <= n; i++) {
if (a[i] >= i) continue; // Skip it directly if it does not satisfy the condition.

// This may count as an optimization. We can see that every index i in valid is smaller than j.
// We do not put all indices of b into valid, because a[i] < i < a[j] < j,
// so the condition can only hold when i < j.
ans += (ll)(lower_bound(valid.begin(), valid.end(), a[i]) -
valid.begin());
// lower_bound finds the first element in valid that is greater than or equal to a[i].
// Therefore, every element **before** it can be used. The length of an interval is r - l + 1.
// Since only the elements before it can be used, we do not add this 1.
valid.push_back(i);
}
cout << ans << '\n';
}
}

G. Good Key, Bad Key

Approach (from the Official Solution)

We can see that alternating between good keys and bad keys for all boxes is always less worthwhile.

Moreover, using good keys consecutively on the earlier boxes is more worthwhile. (In other words, use good keys on a prefix.)

Suppose we use a bad key before a good key. Then the profit we obtain is:

ai2+(ai+12k)\lfloor \frac{a_i}{2} \rfloor + (\lfloor \frac{a_{i + 1}}{2} \rfloor - k)

But if we first use a good key and then a bad key, the profit is:

(aik)+ai+12(a_i - k) + \lfloor \frac{a_{i + 1}}{2} \rfloor

Clearly, using the good key first is more worthwhile.

A more intuitive explanation is that regardless of which key is used first, kk is subtracted from the profit. However, if we use the bad key first, the profits of two boxes are halved, whereas if we use the good key first, only the profit of one box is halved.

Therefore, we only use bad keys in the final part. In some cases where kk is relatively large, halving aia_i may be more worthwhile than subtracting kk.

Thus, we only need to enumerate a dividing point between the use of good and bad keys. Use good keys before this point and bad keys after it.

Let this dividing point be xx.

When bad keys are used after the dividing point xx, the profit from each box becomes:

ax+i=ax+i÷2ia_{x + i} = \lfloor a_{x + i} \div 2^{i} \rfloor

We can see that this 2i2^i grows very quickly, and beyond some point aia_i becomes 00. There is no need to continue calculating beyond that point.

Because the maximum aia_i is 10910^9, there is no need to calculate after log2(109)30log_2(10^9) \approx 30. (Alternatively, consider continually shifting a number to the right; after some point, its entire binary representation contains no 11s.)

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <bits/stdc++.h>
using namespace std;
#define ll long long
// author: ttzytt (ttzytt.com)
// ref: https://codeforces.com/blog/entry/104786
int main() {
int t;
cin >> t;
while (t--) {
int n, k;
cin >> n >> k;
vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
ll psum = 0, ans = 0;
// psum is the profit obtained by using good keys.
for (int i = -1; i < n; i++) {
if (i != -1) psum += (ll)(a[i] - k);
ll cur = psum;
// Enumerate i as the dividing point.
for (int j = i + 1; j < min(n, i + 32); j++) {
// There is no need to continue calculating beyond i + 32.
int bkval = a[j];
bkval >>= (j - i); // i + 1 is divided by 2, i + 2 is divided by 4, ...
cur += bkval;
}
ans = max(ans, cur);
}
cout << ans << endl;
}
}