【CF578E】Walking!(貪心)
阿新 • • 發佈:2020-12-02
- 給定一個長度為\(n\)的由
L
和R
構成的字串\(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\)
可以證偽的貪心
考慮一個貪心,就是每次儘可能選取長度最大的符合條件的子序列,然後重複取若干次直至把整個序列取完。
這一過程的實現是非常簡單的,只要對L
和R
兩種字元各開一個\(set\),每次\(lower\_bound\)找後繼就好了。
然而,這個貪心是會被卡掉的。
唯一一種\(Hack\)資料
會被卡掉是毋庸置疑的,但能卡掉這個貪心的資料實際上只有一種。
以L
為例,如果它選中的R
後面全都是R
,且在此之前還有R
沒有匹配,這時候貪心就會有問題。
因為根據貪心的思路我們會先匹配後面的R
再折回去找之前的L
和R
,那麼不如先回去匹配前面的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; }