Codeforces 939E Maximize! 离散 三分

题目传送门↓
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

猜你喜欢

转载自www.cnblogs.com/smoncaff/p/12293275.html
今日推荐