Codeforces 578E. Walking!(贪心+线段树)

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 N1105

题解

  • 观察样例再自己手玩一下,很快就发现可以贪心,
  • 首先显然若’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;
} 
哈哈哈哈哈哈哈哈哈哈
原文地址:https://www.cnblogs.com/LZA119/p/13910029.html