【CF578E】Walking!(贪心)

点此看题面

  • 给定一个长度为\(n\)的由LR构成的字符串\(s\)。
  • 求构造一个排列\(p_i\),满足\(\forall i\in[1,n),s[p_i]\not=s[p_{i+1}]\),且满足\(p_i>p_{i+1}\)的\(i\)的个数最小。
  • \(n\le10^5\)

可以证伪的贪心

考虑一个贪心,就是每次尽可能选取长度最大的符合条件的子序列,然后重复取若干次直至把整个序列取完。

这一过程的实现是非常简单的,只要对LR两种字符各开一个\(set\),每次\(lower\_bound\)找后继就好了。

然而,这个贪心是会被卡掉的。

唯一一种\(Hack\)数据

会被卡掉是毋庸置疑的,但能卡掉这个贪心的数据实际上只有一种。

L为例,如果它选中的R后面全都是R,且在此之前还有R没有匹配,这时候贪心就会有问题。

因为根据贪心的思路我们会先匹配后面的R再折回去找之前的LR,那么不如先回去匹配前面的R再一路匹配下来。

只要特判一下这种情况就可以了。

代码:\(O(nlogn)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
using namespace std;
int n,v[N+5];char s[N+5];set<int> S[2];
I int Solve(RI x)//第一个字符为x
{
	RI i,t=0;for(i=1;i<=n;++i) S[s[i]=='R'].insert(i);//分字符插入set
	#define Nxt(x,p) S[x].lower_bound(p)//后继
	#define Mn(x) *S[x].begin()//最小值
	RI p=0;for(i=1;i<=n;S[x].erase(v[i]=p),++i,x^=1)
		p=Nxt(x,p)==S[x].end()?(++t,Mn(x)):*Nxt(x,p),//找到下一位
		Nxt(x^1,p)==S[x^1].end()&&p^Mn(x)&&Mn(x)<Mn(x^1)&&(p=Mn(x),++t);//如果之后全是同样的字符,不如先选前面的
	return t;
}
int main()
{
	RI i,t=0;for(scanf("%s",s+1),n=strlen(s+1),i=1;i<=n;++i) s[i]=='L'?++t:--t;//比较两种字符谁更多
	for(printf("%d\n",t?Solve(t==-1):Solve(s[1]=='R')),i=1;i<=n;++i) printf("%d ",v[i]);return 0;
}
上一篇:【CF1015D】Walking Between Houses


下一篇:CF1296C Yet Another Walking Robot 题解