题解 CF11E Forward, march!
题意
给定一个仅由 \(L,R,X\) 组成的模式串和 \(LR\) 交替组成的无限长的文本串,要求在模式串中添加一些 \(X\),使得模式串中任意两个相同的相邻字符(首尾两个也算相邻)均为 \(X\),同时将修改后的模式串复制一遍得到的新模式串与文本串前缀相同的字符数除以新模式串长度最大,输出这个最大值。
题解
YLWang 的题解思路很清晰,我主要是来补个证明。
首先将任意两个相同的相邻字符(且不为 \(X\))之间添加一个 \(X\)。
这时候就有一个问题,首尾两个字符之间有两个位置可以添加。
结论 \(1\):如果首尾字符均为 \(L\),那么添加一个 \(X\) 在最后;否则添加一个 \(X\) 在最前。
证明最后给出。
结论 \(2\):修改后的模式串串长为偶数时的最大值即为答案。
证明:假设答案最大时修改后的模式串串长为奇数,那么答案即为「修改后的模式串中 \(L,R\) 字母个数和」除以「修改后的模式串长度的两倍」。
如果第一次和第二次对答案的贡献不同,我们可以在开头或结尾添加一个 \(X\) 使得答案变为「第一次和第二次答案的较大值」除以「修改后的模式串长度 \(+1\)」,这显然比上面要大,矛盾。
所以两次的答案相同,又由于此时一个字符在将修改后的模式串复制一遍得到的新模式串中对答案恰好只会贡献一次,即该模式串中共有偶数个 \(L,R\),有奇数个 \(X\)。
现在把该模式串中 \(L,R\) 的位置看作 \(0,1\),分别表示该模式串中这位与文本串前缀不同/相同。
那么每添加一个 \(X\) 就相当于对模式串之后的位置全异或上 \(1\)。
我们按照这种方式让模式串最后所有数字的位置都为 \(1\),这至多需要「模式串中 \(L,R\) 字母个数」步。
如果最后模式串长为奇数,我们在最后强行加上一个 \(X\),此时答案至少为「修改后的模式串中 \(L,R\) 字母个数和」除以「修改后的模式串长度 \(+\) 模式串中 \(L,R\) 字母个数 \(+\) 1」,而该模式串中有奇数个 \(X\),也就是至少有一个 \(X\),那么「修改后的模式串长度 \(+\) 模式串中 \(L,R\) 字母个数 \(+\) 1」不大于「修改后的模式串长度的两倍」,即现在的答案不劣于之前的答案,得证。
然后就可以二分答案 \(mid\) 设计 dp,具体而言,设 \(f_{i,0/1}\) 表示当前匹配到原串的第 i 位,且当前位教官的命令是 \(R/L\) 时的最大值。
转移(按顺序进行):
\(f_{i,0}=f_{i,1}+[s_i=R]-mid\)
\(f_{i,1}=f_{i,0}+[s_i=L]-mid\)
\(f_{i,0}=max(f_{i,0},f_{i,1}-mid)\)
\(f_{i,1}=max(f_{i,1},f_{i,0}-mid)\)
边界是 \(f_{0,0}=0,f_{0,1}=-mid\),答案可行当且仅当 \(f_{len,0}\ge0\)。
于是我们就可以给出结论 \(1\) 的证明了。
证明:举首尾字母均为 \(L\) 为例(\(R\) 同理),此时在最后面放一个 \(X\) 一定是最优的。
首先,最前面与最后面的地方 \(X\) 的个数只可能为 \(1\) 或 \(2\),因为为 \(0\) 不符题意,大等于 \(3\) 时必有两个 \(X\) 在同一地方(最前或最后),把它们一起删去答案更优。
如果最后面有 \(X\) 了,那就符合我们的结论,因此我们考虑最后面没有 \(X\) 的情况,即最前面有 \(1\) 或 \(2\) 个 \(X\)。
-
最前面有 \(2\) 个 \(X\),那把它们删去并加到最后面依旧符合题意且答案不变。
-
最前面有 \(1\) 个 \(X\),那么我们计算一下到第一个 \(L\) 时的 dp 值。
假设去掉这个 \(X\),第一位即为第一个 \(L\),此时
\(f_{0,0}=0,f_{0,1}=-mid,f_{1,0}=1-2mid,f_{1,1}=1-mid\)。
假设保留这个 \(X\),第一位为 \(X\),第二位为第一个 \(L\),此时
\(f_{0,0}=0,f_{0,1}=-mid,f_{1,0}=-2mid,f_{1,1}=-mid,f_{2,0}=1-4mid,f_{2,1}=1-3mid\)。
可以发现此时第一个 \(L\) 的位置上仅仅是 dp 值同时减小 \(2mid\),而相对之间的差不变,不影响后面的转移。
因此到最后时,保留 \(X\) 比去掉 \(X\) 的两个 dp 值都小了 \(2mid\),再算上去掉 \(X\) 之后最后还应补一个 \(X\) 的影响,保留 \(X\) 比去掉 \(X\) 的两个 dp 值还是都小了 \(mid\),因此去掉 \(X\)(即把 \(X\) 调到最后)之后二分答案可以二分的更大,得证。
Code
#include<cstdio>
#include<cstring>
#define eps 1e-9
int len=0,len1;
double l=0,r=100,mid;
char s[2000002],s1[1000002];
double f[2000002][2];
inline double max(double x,double y)
{
return x>y? x:y;
}
inline bool check(double x)
{
f[0][0]=0,f[0][1]=-x;
for(int i=1;i<=len;++i)
{
f[i][0]=f[i-1][1]+(s[i]=='R')-x;
f[i][1]=f[i-1][0]+(s[i]=='L')-x;
f[i][0]=max(f[i][0],f[i][1]-x);
f[i][1]=max(f[i][1],f[i][0]-x);
}
return f[len][0]>=0;
}
int main()
{
scanf("%s",s1+1),len1=strlen(s1+1);
if(s1[1]=='R' && s1[len1]=='R')s[++len]='X';
s[++len]=s1[1];
for(int i=2;i<=len1;++i)
{
if(s1[i]!='X' && s1[i]==s1[i-1])s[++len]='X';
s[++len]=s1[i];
}
if(s1[1]=='L' && s1[len1]=='L')s[++len]='X';
while(r-l>eps)
{
mid=(l+r)/2;
if(check(mid/100))l=mid;
else r=mid;
}
return 0&printf("%.6lf",(int)(r*1000000)/1000000.0);
}