题解:AT_abc469_e [ABC469E] Pro Exam Eligibility

题目链接:洛谷 AtCoder


貌似我的做法和官方题解不一样?

首先分治,每一层只处理过分治中点的区间。设当前层对应的区间是 \([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\) 匹配能产生更大的答案就需要分类讨论了:

  1. \([x,mid]\)\([mid+1,y_1]\)\(\text{o}\) 的占比相同。此时选 \(y_1\)\(y_2\) 的答案相同。

  2. \([x,mid]\)\(\text{o}\) 的占比更大,此时选 \(y_1\) 更优。

  3. \([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;
}

有趣的是,因为数据较弱,只做一遍的代码即可通过所有测试点。(我赛时就是这么过的。)

posted @ 2026-08-24 19:02  ShanLing3  阅读(4)  评论(0)    收藏  举报
//雪花飘落效果