题目传送门↓http://codeforces.com/problemset/problem/939/E
题意:有一最初为空的不下降序列,有两种操作,一种是在序列里增加一个不小于序列尾的整数,另一种是查询当前序列中的某个子集,其最大值为max,平均值为mean,使得子集的max-mean在当前序列的所有子集中最大,输出这个max-mean。
思考:
1.序列最大值是否入选,假设不入选,则此时答案的子集中,最大值为max1,和为sum1,元素个数为k则答案可表示为max1 - sum1 / k;若在此答案的基础上更换序列的最大值,此时最大值假设比max1大d,则答案表达式为 max + d - (sum + d) / k ,对比原式,变化值 Δ= d - d/k >=0 ,因此对每个子集,必定包含序列的最大值。
2.假设当前答案的子集平均值是mean,那么对于这个序列中所有小于mean的元素都应该入选(可以拉低平均值),对于大于mean的元素也可能会入选(加大分母),但也是从小到大排着;因此子集除最大值外的其他部分必定是从第一个数开始的连续数字。
3.关于子集的长度与max- mean的关系是个单峰函数,因此对子集的长度采用三分法确定
1 #include<iostream> 2 #include<cstdio> 3 #define LL long long 4 5 using namespace std; 6 7 LL sum[500010],q,cur; 8 9 int main() 10 { 11 scanf("%d",&q); 12 while(q--) 13 { 14 int typ,aa; 15 scanf("%d",&typ); 16 if(typ==1) 17 { 18 scanf("%d",&aa); 19 sum[++cur]=sum[cur-1]+aa; 20 // for(int i=1;i<=cur;i++) 21 // printf("%lld ",sum[i]); 22 } 23 24 else 25 { 26 int a=sum[cur]-sum[cur-1]; 27 int l=1,r=cur-1; 28 29 while(l<r) 30 { 31 int mid2=(l+r+1)>>1,mid1=(l+mid2)>>1; 32 33 double an1=(double)(sum[mid1]+a)/(mid1+1),an2=(double)(a+sum[mid2])/(mid2+1); 34 35 if(an1>an2) l=mid1+1; else if(an2>an1) r=mid2-1; 36 else if(r-l>1) r=mid2;// 37 else break; 38 39 } 40 double ans=a-(double)(sum[l]+a)/(l+1); 41 42 printf("%.10lf\n",ans); 43 } 44 } 45 46 return 0; 47 }
注意事项:31行,避免r=l+1时,mid2=l,mid1=l,r取不到
37行,当相邻两个值相等的时候,已经找到答案,可以break
36行,避免因为向下取整导致死循环;当r=l+2时,且an1=an2时,mid1=l,mid2=l+1,若更新l=mid1,则进入死循环,因此更新r=mid进一步缩小范围,然后在下一个循环break
原文地址:https://www.cnblogs.com/smoncaff/p/12293275.html