「GLR-R3」雨水
给定长为 $n$ 的序列 $a$,你需要选定 $a$ 的一个长度为偶数 $m$ 的子序列 $b$,对于任意 $1\le i\le \dfrac m2$,交换 $b_{2i-1}$ 和 $b_{2i}$,最后将 $a$ 中原来 $b$ 的位置替换成交换过后的 $b$,求如何选取子序列使得 $a$ 的字典序最小。
首先我们对交换的每对数进行考虑。如果将交换的一对数看做区间,那么根据子序列的性质可以知道这些区间之间不会相互覆盖或者有交集。也就是说,这些区间之间都是相对独立的。
字典序最小意味着什么呢?如果存在两个长度为 $k$ 的序列 $s$ 和 $t$,那么即使对于任意 $2\le i\le k$,有 $s_i\lt t_i$,但如果一旦 $s_1>t_1$,那么就前功尽弃,此时 $s<t$。
这给我们的启发是,如果要使 $a$ 字典序最小,那么即使后面再怎么小,前面很大都没有用。因此,我们需要尽可能使 $a_1$ 小,在 $a_1$ 最小的情况下,尽可能使 $a_2$ 小,以此类推。
如何使 $a_1$ 最小?这个时候,我们不需要考虑 $a_2$ 的大小情况,因为 $a_1$ 是最重要的。
初步思路是,找出在下标在 $2\sim n$ 时的最小值,然后与 $a_1$ 交换。
这个时候会出现两个问题:
-
如果最小值比 $a_1$ 大,怎么处理?
这说明 $a_1$ 已经是最小的了,不需要进行交换。这个时候我们就处理下个元素 $a_2$。
-
如果有多个最小值,怎么处理?
先给出结论:选最靠后的那一个。
很好证明:因为 $a_1$ 此时比最小值大,那么交换以后,由于字典序越靠前权重越大,所以如果把 $a_1$ 放在最前面,字典序一定是比把其放在最后面要大的。
但是,根据我们前面提到的“区间独立性”,前面的最小值都无法进行改变了,如果后面最小值需要进行改变,怎么办呢?
我们考虑对最小值模拟刚才的运算。因为这个最小值是下标区间 $2\sim n$ 的最小值,而最小值的位置又在 $a_1$ 后面,所以对这个最小值求其后面的最小值肯定是比其本身要大或者相等的。
这也就说明,这个区间内的最小值绝对不会进行改变。
这个时候再回到我们的“区间独立性”。假定这个时候我们已经将 $a_1$ 与 $a_{t}$ 交换,其中 $t$ 表示最小值的下标,根据区间独立性,下一个区间的开头必然 $> t$。于是,我们只要将上文的所有 $a_1$ 替换为 $a_{t+1}$ 重复一遍上述过程即可。整个过程用 while
循环实现。
注意,此处 while
循环的结束条件并非 $s\le n$($s$ 表示每个区间的开头位置),因为每个区间长度至少为 $2$,所有结束条件为 $s\le n-1$。
这样,我们的最劣时间复杂度为 $\Theta(n^2)$,可能不能通过此题。如何优化呢?
注意到求最小值部分可以预处理出后缀最小值的位置,每次循环 $\Theta(1)$ 查询即可。这样,我们可以把时间复杂度优化至 $\Theta(n)$,容易通过此题。
到这里思路部分基本上讲完了。还有一些小的细节,比如说怎么对 $2^{64}$ 取模:
我们知道 unsigned long long
的数据范围是 $[0,2^{64})$。刚刚好是一个数对 $2^{64}$ 取模的结果的取值范围。因此使用 unsigned long long
存储答案,不需要取模,全程自然溢出即可。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e7 + 1; // Set a right value according to your solution.
long long int n, a[MAXN + 1];
int suf[MAXN + 1];
namespace Generator {
unsigned long long k1, k2;
int thres;
inline unsigned long long xorShift128Plus() {
unsigned long long k3 = k1, k4 = k2;
k1 = k4, k3 ^= (k3 << 23), k2 = k3 ^ k4 ^ (k3 >> 17) ^ (k4 >> 26);
return k2 + k4;
}
inline void generate() {
for (register int i = 1; i <= n; ++i) {
a[i] = xorShift128Plus() % thres;
}
}
} // namespace Generator.
void init() {
suf[n] = n;
for (int i = n - 1; i >= 1; i--) {
suf[i] = (a[i] < a[suf[i + 1]] ? i : suf[i + 1]);
}
}
unsigned long long solve() {
int s = 1;
while (s <= n - 1) {
int m = suf[s + 1];
if (a[s] > a[m]) {
swap(a[s], a[m]);
s = m + 1;
} else s++;
}
unsigned long long ans = 0;
for (register int i = 1; i <= n; ++i) {
// cout << a[suf[i]] << " ";
ans += (unsigned long long) a[i] * (unsigned long long) i;
}
// cout << endl;
return ans;
}
int main() {
scanf("%d", &n);
scanf("%llu %llu %d", &Generator::k1, &Generator::k2, &Generator::thres);
Generator::generate();
init();
cout << solve();
return 0;
}