codeforces 939E - Maximize (三分 / 双指针)

传送门

题意:
给定一个空集合S,操作1是每次加入一个数,保证不会小于当前集合的最大数,操作2是询问,从该集合选出一个子集,满足子集最大值减去元素平均值最大,输出这个最大值。

1<=q<=5e5

首先可以感性地想到选出来的子集的2个性质,

①选出来的最大元素一定是集合S的最大值,即:每次都选最新加入的元素。
②其余的元素是集合S的一个连续的前缀(按升序排列之后) .

为什么呢?
①:在这里插入图片描述

②:如果我们选的是s[1],s[2],s[4],s[5],那么如果我们用s[3]去代替s[4],总和一定会减小,所以被挑出来的元素中,除去最大值外,一定是一个连续前缀。

所以我们就可以双指针了,r指向当前加入的最新元素,代表最大值的位置,l表示最优的前缀位置,使得平均值最小. 当r++,l也右移动寻找到当前最小的位置。

其实这个平均值是以l为变量的下凹函数,也就是说,随着加入的元素越来越多,平均值会先下降再上升。 而r++,只会让极值点右移。

所以我们也可以用三分法求函数峰值。

双指针:

#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ull unsigned long long
#define ll long long
#define pii pair<int, int>
const int maxn = 5e5 + 10;
const ll mod = 1e9 + 7;
const ll inf = (ll)2e14+5;
const int INF = 1e9 + 7;
const double pi = acos(-1.0);
ll inv(ll b){
    
    while(b==1)return 1;return(mod-mod/b)*inv(mod%b)%mod;}
inline ll read()
{
    
    
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){
    
    while(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){
    
    x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
ll sum[maxn];
int x;
inline double cal(int idx)//返回当前平均值
{
    
    
    return 1.0*(x+sum[idx]) / (1.0*idx+1.0);
}
int main()
{
    
    
    int q;
    scanf("%d",&q);
    int cnt=0;
    int f;
    int x0=0;//左指针
    while(q--)
    {
    
    
        scanf("%d",&f);
        if(f==1) 
        {
    
    
            scanf("%d",&x);
            sum[++cnt]=sum[cnt-1]+x;
        }
        else 
        {
    
    
            while(x0+1<cnt && (cal(x0+1) < cal(x0))) 
            {
    
    
                x0++;
            } 
            printf("%.10f\n",1.0*x-cal(x0));
        }
    }
    return 0;
}

三分:

#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ull unsigned long long
#define ll long long
#define pii pair<int, int>
const int maxn = 5e5 + 10;
const ll mod = 1e9 + 7;
const ll inf = (ll)2e14+5;
const int INF = 1e9 + 7;
const double pi = acos(-1.0);
ll inv(ll b){
    
    while(b==1)return 1;return(mod-mod/b)*inv(mod%b)%mod;}
inline ll read()
{
    
    
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){
    
    while(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){
    
    x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
ll sum[maxn];
int x;
inline double calc(int idx)
{
    
    
    return 1.0*(x+sum[idx]) / (1.0*idx+1.0);
}
int main()
{
    
    
    int q;
    scanf("%d",&q);
    int cnt=0;
    int f;
    while(q--)
    {
    
    
        scanf("%d",&f);
        if(f==1) 
        {
    
    
            scanf("%d",&x);
            sum[++cnt]=sum[cnt-1]+x;
        }
        else 
        {
    
    
     
            double ans=0;
            int l=1,r=cnt-1;
			while(r>=l)
			{
    
    
				int mid1=(r-l)/3+l,mid2=r-(r-l)/3;
				double q=calc(mid1),w=calc(mid2);
				if(q>w)	l=mid1+1,ans=max(ans,1.0*x-w);
				else	r=mid2-1,ans=max(ans,1.0*x-q);	
			}
            printf("%.10f\n",ans);
        }
    }
    return 0;
}

和二分要观察出单调性一样,
三分的题要观察得出单峰函数。

猜你喜欢

转载自blog.csdn.net/qq_46030630/article/details/121344444
今日推荐