首先,我们对 (a) 数组进行排序。
注意到:
如果直接将一个从小到大排序的数列交替的放在天平两边。即:奇数位放在 (A) 侧,偶数位放在 (B) 侧。
就会发现每次放置砝码,都是刚刚放置了砝码的那一侧较重。
证明:
放了偶数个的时候:
(A) 侧放了 (a_1,a_3,...a_{2k-1}),(B) 侧放了 (a_2,a_4,...a_{2k}),
显然 (a_i < a_{i + 1}),(B) 侧更重
放了奇数个的时候:
(A) 侧放了 (a_1,a_3,...a_{2k-1},a_{2k+1}),(B) 侧放了 (a_2,a_4,...a_{2k}),
(ecause a_i < a_{i + 1})
( herefore a_3+...+2_{2k+1}>a_2+a_4...+a_{2k})
( herefore a_1+a_3+...+2_{2k+1}>a_2+a_4...+a_{2k})
(A) 侧更重
现在如果有一个已经排好序的数组按如上方式被放置在天平上。
- 若取出最大的数,那么天平一定会改变他的状态。
- 若取出最小的数,那么天平的状态将不变。
无论取出最大数还是最小数,天平上都仍然是一个新的排好序的数组按照之前的方式被放置。
那么我考虑一开始就直接放一个排好序的数组,然后第 (i) 次我取出一个数 (x),也就是答案中第 (n-i+1) 次我放置的数。很显然每次都能取到数,所以一定存在答案。
上面那个东东的实现方法有很多。
下面是CF官方题解的实现方法,非常简单。
先对 (a) 进行排序。
当我们从 (L) 转到 (R) 或从 (L) 转到 (R) 时,会发生一次转换。
我们设有 (cnt) 次转换。先将 (a_{n-cnt}) 放置在 (s_1) 侧。
现在维护两个指针:左指针 (l) 和 右指针 (r),初始指向 (n-cnt) 处。
对于 (s_i(iin [2,n])) ,
- 如果没有发生转换,即 (s_i eq s_{i-1}),减小左指针,将左指针指向的砝码放在与上一次放置左指针指向的砝码时所放置的相反的一侧
- 如果发生了一次转换,即 (s_i = s_{i-1}),增大右指针,将右指针指向的砝码放在与上一次放置右指针指向的砝码时所放置的相反的一侧,也就是此时的 (s_i) 侧。
证明构造的是合法的解:
考虑 ([1,i]) 与 ([1,i-1]) 时天平的状态。
- 如果没有发生转换,即 (s_i eq s_{i-1}),从 ([1,i]) 的天平中取出了最小数,变成了 ([1,i-1]) 的天平。
- 如果发生了一次转换,即 (s_i = s_{i-1}),从 ([1,i]) 的天平中取出了最大数,变成了 ([1,i-1]) 的天平。
用了 (cnt) 次右指针,(n-cnt-1) 次左指针,还有一次是 (s_1)。所以一定能通过这个方法找到合法的解。
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mkp make_pair
#define pb push_back
#define PII pair<int, int>
#define PLL pair<ll, ll>
const int N = 2e5 + 10;
int n, cnt, a[N], s[N], ans[N], op[N];
char ch[N];
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; i++)
scanf("%d", &a[i]);
sort(a + 1, a + n + 1);
scanf("%s", ch + 1);
for(int i = 1; i <= n; i++)
s[i] = (ch[i] == 'R');
for(int i = 1; i < n; i++)
cnt += (s[i] != s[i + 1]);
int l = n - cnt, r = n - cnt;
for(int i = 1; i <= n; i++) {
if(i == 1) {
ans[i] = n - cnt;
op[ans[i]] = s[i];
} else {
if(s[i] == s[i - 1]) {
ans[i] = --l;
op[ans[i]] = op[l + 1] ^ 1;
} else {
ans[i] = ++r;
op[ans[i]] = op[r - 1] ^ 1;
}
}
}
for(int i = 1; i <= n; i++)
printf("%d %c
", a[ans[i]], (op[ans[i]] ? 'R' : 'L'));
return 0;
}