Codeforces 578E. Walking!
题目大意
- 给出一个长度为
N
N
N字符串
S
S
S,由’L’和‘R’组成,
- 求一个
N
N
N的排列
P
P
P,使得
S
P
i
≠
S
p
i
+
1
S_{P_i}≠S_{p_{i+1}}
SPi�=Spi+1,也就是说要满足选择的相邻两个都不相同,
- 同时要最小化
p
i
>
p
i
+
1
p_i>p_{i+1}
pi>pi+1的个数,也就是使排列相邻的两个数前一个大于后一个的数量最少。
- 数据保证有解。
-
N
≤
1
∗
1
0
5
N≤1*10^5
N≤1∗105
题解
- 观察样例再自己手玩一下,很快就发现可以贪心,
- 首先显然若’R’的数量多,则第一个从’R’开始,反之同理;若两种字符一样多,则从哪个开始都可以。
- 先尽量选择左边的,然后向右继续选最靠近它的符合条件的,这样一直下去,直到最后面选不了了,再回到最左边一个符合条件的,以此类推不断往复,似乎就可以求出最小答案,而且特别显然,可以用线段树维护。
- 但是发现有一种特殊情况,
- 假如出现“RLLR”的情况,且当前在第二个’L’的位置,按照贪心我们会先向右找一个’R’,然后发现没有了,返回左边第一个’L’,然后它后面也没有‘R’了,又往左一次返回到第一个’R’,这样返回了两次,
- 而如果先直接返回第一个’R’,接下来就可以一直顺着走完,那么只返回了一次,更优。
- 所以需要判断一下,如果当前字符为’L’,它右边的第一个‘R’右边剩下的全是‘R’,且从左边起第一个剩下的也是‘R’,那么直接回到最左边的‘R’;如果当前为‘R’同理。
- 这样答案是正确的。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 100010
char st[N];
int a[N], n;
int f[2][N * 4], ans[2][N], bz[N], fa[N], s[2], nxt[N];
void ins(int v, int l, int r, int x, int o, int c) {
if(l == r) f[o][v] = c;
else {
int mid = (l + r) / 2;
if(x <= mid) ins(v * 2, l, mid, x, o, c);
else ins(v * 2 + 1, mid + 1, r, x, o, c);
f[o][v] = min(f[o][v * 2], f[o][v * 2 + 1]);
}
}
int find(int v, int l, int r, int x, int y, int o) {
if(l == x && r == y) return f[o][v];
int mid = (l + r) / 2;
if(y <= mid) return find(v * 2, l, mid, x, y, o);
if(x > mid) return find(v * 2 + 1, mid + 1, r, x, y, o);
return min(find(v * 2, l, mid, x, mid, o), find(v * 2 + 1, mid + 1, r, mid + 1, y, o));
}
int get(int k) {
if(fa[k] == k) return k;
return fa[k] = get(fa[k]);
}
int ss = 0;
int ct(int l, int o) {
if(l >= n) return n + 1;
int l1 = find(1, 1, n, l + 1, n, o);ss++;
while(bz[l1]) {
if(l1 == n) return n + 1;
int l2 = nxt[l1];
fa[l1] = get(l2);
l1 = fa[l2];
}
return l1;
}
void solve(int o) {
int i;
s[0] = s[1] = n + 1;
memset(bz, 0, sizeof(bz));
for(i = n; i; i--) {
nxt[i] = s[a[i]];
fa[i] = i;
s[a[i]] = i;
ins(1, 1, n, i, 0, s[0]);
ins(1, 1, n, i, 1, s[1]);
}
fa[n + 1] = n + 1;
int l = 0, r = o; ans[o][0] = 0;
for(i = 1; i <= n; i++) {
int tt = ct(0, r);
if(ct(ct(l, r), 1 - r) == n + 1 && tt < ct(0, 1 - r)) {
int l1 = tt;
if(l1 < l) ans[o][0]++;
l = ans[o][i] = l1;
bz[l] = 1;
r = 1 - r;
continue;
}
int l1 = ct(l, r);
if(l1 == n + 1) {
ans[o][0]++;
l1 = ct(0, r);
}
l = ans[o][i] = l1;
bz[l] = 1;
r = 1 - r;
}
}
int main() {
scanf("%s", st + 1);
n = strlen(st + 1);
int t[2] = {0, 0}, i;
for(i = n; i; i--) {
a[i] = (st[i] == 'R');
t[a[i]]++;
}
ans[0][0] = ans[1][0] = n + 1;
if(t[1] >= t[0]) solve(1);
if(t[0] >= t[1]) solve(0);
int o;
if(ans[0][0] < ans[1][0]) o = 0; else o = 1;
printf("%d
", ans[o][0]);
for(i = 1; i <= n; i++) printf("%d ", ans[o][i]);
return 0;
}