题意:
给定一个空集合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;
}
和二分要观察出单调性一样,
三分的题要观察得出单峰函数。