第二十一次CCF软件能力认证B题-期末预测之最佳阈值 知识点:前缀和

67 阅读2分钟

3298. 期末预测之最佳阈值 - AcWing题库

题意解析

左边是数值,右边是布尔值,每次我们要选一个数值x,计算一下大于等于x的数值的且布尔值为1的个数,小于x的数值的且布尔值为0的个数,求一下累加和的最大值。 image.png

例如,我们选择3这个数值,那么大于等于它的数值且布尔值为1的个数有3个,小于它的数值且布尔值为0的个数有2个。一共有5个满足的数:

image.png

假设我们选取5这个数值,那么满足条件的有4个:

image.png

当我们选取第三个1的时候,满足条件的有5个:

image.png

已经可以确定最大满足条件的个数就是5,然后因为数值3的满足条件的个数也是5,且数值3比数值1的字典序大,所以输出3。

那么现在问题就转化为了我们每枚举一个数,就求一下这个数前面有多少个0,后面有多少个1:

image.png

这实际上就是求区间内元素个数,我们可以通过前缀和来求。

我们预处理一个s数组,si0表示i前面有多少个0,si1表示i前面有多少个1。

现在我们求i前面有多少个0就直接用s[0][i-1]求。我们想求i后面有多少个1就用s[1][n]-s[1][i-1]。

code

#include<bits/stdc++.h>
using namespace std;
#define x first
#define y second
const int N=1e5+10;
typedef pair<int,int>PII;
PII q[N];
int s[2][N];

int main()
{
    int n;cin>>n;
    for(int i=1;i<=n;i++)cin>>q[i].x>>q[i].y;
    
    sort(q+1,q+n+1);
    
    for(int i=0;i<2;i++)
    {
        for(int j=1;j<=n;j++)  //前缀和最好下标从1开始
        {
            s[i][j]=s[i][j-1]+(q[j].y==i);
        }
    }
    
    int cnt=-1,res;
    for(int i=1;i<=n;i++)
    {
        int t=s[0][i-1]+s[1][n]-s[1][i-1];
        if(t>=cnt)cnt=t,res=q[i].x;
        //cout<<"t:"<<t<<" "<<"res:"<<res<<endl;
        while(i+1<=n&&q[i+1].x==q[i].x)i++;
        
    }
    
    cout<<res<<endl;
    return 0;
}

image.png