题解:AT_abc469_e [ABC469E] Pro Exam Eligibility
貌似我的做法和官方题解不一样?
首先分治,每一层只处理过分治中点的区间。设当前层对应的区间是 \([l,r]\),\(mid=\lfloor\frac{l+r}{2}\rfloor\),下文只讨论这一层中的情况。
对于每个 \(x\in [l,mid]\),找到最小的 \(y\in [mid+1,r]\)(可能不存在),使得区间 \([x,y]\) 中有至少 \(k\) 个字符 \(\text{o}\),那么区间 \([x,y],[x,y+1],\dots ,[x,r]\) 都是合法的。对 \([mid+1,y]\) 做后缀 \(\max\) 维护 \(\text{o}\) 占比最大的位置,容易证明选对应 \(\text{o}\) 占比更高的位置作为右端点一定更优,就可以对每个 \(x\) 快速地找到 \(y\)。
做法就这么简单,说完了。说完了吗?没有。仔细思考会发现,占比相等的情况并不好处理。具体地说,设 \(mid+1\leq y_1<y_2\leq r\),且 \([mid+1,y_1]\) 和 \([mid+1,y_2]\) 中 \(\text{o}\) 的占比相同,那 \(y_1\) 和 \(y_2\) 谁和 \(x\) 匹配能产生更大的答案就需要分类讨论了:
-
\([x,mid]\) 和 \([mid+1,y_1]\) 中 \(\text{o}\) 的占比相同。此时选 \(y_1\) 和 \(y_2\) 的答案相同。
-
\([x,mid]\) 中 \(\text{o}\) 的占比更大,此时选 \(y_1\) 更优。
-
\([x,mid]\) 中 \(\text{o}\) 的占比更小,此时选 \(y_2\) 更优。
那怎么办呢?很简单!我们任选一种比较方法(大于等于或大于都行),按上述方法做一遍,再翻转区间做一遍,就一定能找到最大的答案。(应该也可以在占比相等的时候维护最小和最大长度,就只用跑一遍,不过好像跑两遍的代码更好写?)
时间复杂度 \(O(n \log n)\).
代码
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+5;
#define pii pair<int,int>
int n,k;
double ans;
pii a[N],b[N],c[N];
char s[N];
void calc(pii *a,pii *b,int t1,int t2)
{
for(int i=t1;i>=1;i--)//后缀 max
{
c[i]=a[i];
if(i<t1&&1ll*c[i].first*c[i+1].second<=1ll*c[i+1].first*c[i].second)
c[i]=c[i+1];
}
for(int i=t2;i>=1;i--)
{
int j=max(1,k-i);
if(j>t1)
break;
ans=max(ans,1.0*(b[i].first+c[j].first)/(b[i].second+c[j].second));
}
}
void solve(int l,int r)
{
if(l==r)
return;
int mid=(l+r)>>1;
solve(l,mid);
solve(mid+1,r);
int tl=0,tr=0;
for(int i=mid;i>=l;i--)//求每个区间的信息
if(s[i]=='o')
{
tl++;
a[tl]={tl,mid-i+1};
}
for(int i=mid+1;i<=r;i++)
if(s[i]=='o')
{
tr++;
b[tr]={tr,i-mid};
}
calc(a,b,tl,tr);//正着做一遍
calc(b,a,tr,tl);//倒着做一遍
}
int main()
{
scanf("%d%d%s",&n,&k,s+1);
if(k==1)//特判
{
for(int i=1;i<=n;i++)
if(s[i]=='o')
{
putchar('1');
return 0;
}
putchar('0');
return 0;
}
solve(1,n);
printf("%.15lf",ans);
return 0;
}
有趣的是,因为数据较弱,只做一遍的代码即可通过所有测试点。(我赛时就是这么过的。)

浙公网安备 33010602011771号