|
| 1 | +--- |
| 2 | +id: usaco-1498 |
| 3 | +source: Gold |
| 4 | +title: The Best Subsequence |
| 5 | +author: Justin Ji |
| 6 | +--- |
| 7 | + |
| 8 | +[Official Analysis (C++)](https://usaco.org/current/data/sol_prob2_gold_feb25.html) |
| 9 | + |
| 10 | +<Spoiler title="Hint 1"> |
| 11 | + |
| 12 | +Let's assume we have some black box function that counts the number of ones in the range $[l, r]$. Let's consider two |
| 13 | +cases: one where the number of ones in our range is less than $K$, and |
| 14 | +if the number of ones is greater than or equal to $K$. How can we |
| 15 | +handle these two cases? |
| 16 | + |
| 17 | +</Spoiler> |
| 18 | + |
| 19 | +<Spoiler title="Hint 2"> |
| 20 | + |
| 21 | +If the number of ones is $\geq K$, then our subsequence can consist of |
| 22 | +all ones. Otherwise, we need to let some amount of zeros into our subsequence. |
| 23 | + |
| 24 | +Let's say the number of ones in our range is $X$. Then, we need to let |
| 25 | +$K-X$ zeros into our range, and we want the first zero to come as far |
| 26 | +back as possible. What does this correspond to in our array? |
| 27 | + |
| 28 | +</Spoiler> |
| 29 | + |
| 30 | +<Spoiler title="Solution"> |
| 31 | + |
| 32 | +As denoted in the hints, let $X$ be the number of ones in our range. |
| 33 | + |
| 34 | +### Case 1: $X \geq K$ |
| 35 | + |
| 36 | +Our entire subsequence is ones, so the answer is $2^k - 1$. |
| 37 | + |
| 38 | +### Case 2: $X < K$ |
| 39 | + |
| 40 | +It's always optimal to take the smallest suffix of $[l, r]$ that |
| 41 | +has $K - X$ zeros. To intuitively understand this, consider the fact that |
| 42 | +we want to maximize the first position where we have a zero in our subsequence. Given that we need to take at least $K-X$ zeros, we at a |
| 43 | +minimum should take the last $K-X$ zeros in our range. Because we are |
| 44 | +trying to maximize our resulting subsequence, it's also optimal to take the ones inside the suffix we end up taking. |
| 45 | + |
| 46 | +### Computing the Result |
| 47 | + |
| 48 | +To compute $2^k$ efficiently, we use |
| 49 | +[modular exponentiation](/gold/modular). This allows us to handle case 1 |
| 50 | +quite easily. |
| 51 | + |
| 52 | +As mentioned in the hints, we want some function to efficiently find the |
| 53 | +number of ones in a given range. Let's first simplify this problem |
| 54 | +to counting the number of ones in a prefix. We can do this by coordinate |
| 55 | +compressing our values, and then calculating prefix sums of the number of ones in our prefix. Careful implementation is required here, as we have to make sure the last interval in our prefix sum is not overcounted. |
| 56 | + |
| 57 | +We can reduce the problem of computing the hash of a range to finding the |
| 58 | +hash of a prefix. For every interval, we store the hash of the entire prefix that includes this interval. Make sure to handle intervals that |
| 59 | +are partially contained in our prefix carefully. Then, the hash of $[l, r]$ is: |
| 60 | + |
| 61 | +$$ |
| 62 | +\texttt{pref\_hash}(r) - 2^{r - l + 1} \cdot \texttt{pref\_hash}(l - 1) |
| 63 | +$$ |
| 64 | + |
| 65 | +To choose the suffix of our range that we hash, we need to find the |
| 66 | +smallest suffix that has $K - X$ zeros in it. This is equivalent to finding the last indice $m$ where the number of zeros in $[m, r]$ is $\geq K-X$. The easiest way to implement this is with binary search, |
| 67 | +although it is possible to cut a log factor by finding the interval |
| 68 | +our indice $m$ is in using lower bound on a prefix sum of zeros. |
| 69 | + |
| 70 | +## Implementation |
| 71 | + |
| 72 | +**Time Complexity:** $\mathcal{O}(Q\log^2N)$ |
| 73 | + |
| 74 | +<LanguageSection> |
| 75 | +<CPPSection> |
| 76 | + |
| 77 | +```cpp |
| 78 | +#include <bits/stdc++.h> |
| 79 | +using namespace std; |
| 80 | + |
| 81 | +using ll = long long; |
| 82 | + |
| 83 | +constexpr int MOD = 1e9 + 7; |
| 84 | + |
| 85 | +int modpow(int base, int exp) { |
| 86 | + int res = 1; |
| 87 | + for (; exp; exp /= 2, base = (1ll * base * base) % MOD) { |
| 88 | + if (exp & 1) { res = (1ll * res * base) % MOD; } |
| 89 | + } |
| 90 | + return res; |
| 91 | +} |
| 92 | + |
| 93 | +int main() { |
| 94 | + int n, m, q; |
| 95 | + cin >> n >> m >> q; |
| 96 | + vector<int> vals = {0, INT_MAX}; |
| 97 | + vector<array<int, 2>> upds(m); |
| 98 | + for (auto &[l, r] : upds) { |
| 99 | + cin >> l >> r; |
| 100 | + r++; |
| 101 | + vals.push_back(l); |
| 102 | + vals.push_back(r); |
| 103 | + } |
| 104 | + |
| 105 | + sort(begin(vals), end(vals)); |
| 106 | + vals.erase(unique(begin(vals), end(vals)), end(vals)); |
| 107 | + |
| 108 | + const auto id = [&](int x) -> int { |
| 109 | + return lower_bound(begin(vals), end(vals), x) - begin(vals); |
| 110 | + }; |
| 111 | + |
| 112 | + const int range_len = vals.size(); |
| 113 | + vector<int> diff(range_len); |
| 114 | + for (const auto &[l, r] : upds) { |
| 115 | + diff[id(l)]++; |
| 116 | + diff[id(r)]--; |
| 117 | + } |
| 118 | + |
| 119 | + vector<int> pref(range_len); |
| 120 | + for (int i = 0; i + 1 < range_len; i++) { |
| 121 | + diff[i + 1] += diff[i]; |
| 122 | + diff[i] &= 1; |
| 123 | + pref[i + 1] = pref[i] + diff[i] * (vals[i + 1] - vals[i]); |
| 124 | + } |
| 125 | + |
| 126 | + vector<int> pref_hash(range_len); |
| 127 | + for (int i = 0; i + 1 < range_len; i++) { |
| 128 | + const int len = vals[i + 1] - vals[i]; |
| 129 | + const int pw2 = modpow(2, len); |
| 130 | + pref_hash[i + 1] = diff[i] * (pw2 - 1 + MOD) % MOD |
| 131 | + + (1ll * pref_hash[i] * pw2) % MOD; |
| 132 | + pref_hash[i + 1] %= MOD; |
| 133 | + } |
| 134 | + |
| 135 | + const auto range_hash = [&](int l, int r) -> int { |
| 136 | + const auto get_pref = [&](int x) -> int { |
| 137 | + const int pos = upper_bound(begin(vals), end(vals), x) - begin(vals) - 1; |
| 138 | + const int pw2 = modpow(2, x - vals[pos] + 1); |
| 139 | + return (1ll * pref_hash[pos] * pw2 + diff[pos] * (pw2 - 1)) % MOD; |
| 140 | + }; |
| 141 | + |
| 142 | + ll raw = get_pref(r) - 1ll * modpow(2, r - l + 1) * get_pref(l - 1); |
| 143 | + return (raw % MOD + MOD) % MOD; |
| 144 | + }; |
| 145 | + |
| 146 | + const auto get_ones = [&](int l, int r) -> int { |
| 147 | + const auto get_pref = [&](int x) -> int { |
| 148 | + const int pos = upper_bound(begin(vals), end(vals), x) - begin(vals); |
| 149 | + return pref[pos - 1] + diff[pos - 1] * (x - vals[pos - 1] + 1); |
| 150 | + }; |
| 151 | + |
| 152 | + return get_pref(r) - get_pref(l - 1); |
| 153 | + }; |
| 154 | + |
| 155 | + for (int t = 0; t < q; t++) { |
| 156 | + int l, r, k; |
| 157 | + cin >> l >> r >> k; |
| 158 | + const int num_ones = get_ones(l, r); |
| 159 | + if (num_ones >= k) { |
| 160 | + cout << modpow(2, k) - 1 << "\n"; |
| 161 | + } else { |
| 162 | + int lo = l, hi = r; |
| 163 | + while (lo < hi) { |
| 164 | + int mid = (lo + hi + 1) / 2; |
| 165 | + bool works = (r - mid + 1) - get_ones(mid, r) >= k - num_ones; |
| 166 | + works ? lo = mid : hi = mid - 1; |
| 167 | + } |
| 168 | + |
| 169 | + const int suffix_hash = range_hash(lo, r); |
| 170 | + const int suffix_ones = get_ones(lo, r); |
| 171 | + const ll res = suffix_hash + modpow(2, k) - modpow(2, k - (num_ones - suffix_ones)); |
| 172 | + cout << (res % MOD + MOD) % MOD << "\n"; |
| 173 | + } |
| 174 | + } |
| 175 | +} |
| 176 | +``` |
| 177 | +
|
| 178 | +</CPPSection> |
| 179 | +</LanguageSection> |
| 180 | +
|
| 181 | +</Spoiler> |
0 commit comments