hdu6070Dirt Ratio 多校题 套路二分

比赛中我看了一眼题目就觉得是二分的套路,跟miaom说,结果发现miaom开始碎觉

miaom醒来以后表示这是道凸包合并(%%%)

我&wzf2000:那您快写啊

miaom:我不会写啊

莫名其妙的忘记了最初二分的思路

赛后看题解,果然是套路移动右端点

假设要mid能达到,那么一定有一组lr满足d(l,r)/(r-l+1)<=mid (d为区间不同数的种数)

d(l,r)+l*mid<=(r+1)*mid

在右端点右移1的时候只有一个后缀的d会改变,于是能轻松维护所有左端点对应的值

 1 #include <bits/stdc++.h>
 2 #define mi (l+r>>1)
 3 #define eps 0.00001
 4 #define INF 2000000000
 5 using namespace std;
 6 int n,T;
 7 int a[200001],b[200001];
 8 double tr[8000001],flag[8000001];
 9 void add(int now,int l,int r,int x,int y,double z)
10 {
11     if(l==x && r==y)
12     {
13         tr[now]+=z;
14         flag[now]+=z;
15         return;
16     }
17     if(x<=mi) add(now<<1,l,mi,x,min(mi,y),z);
18     if(y>mi) add(now<<1|1,mi+1,r,max(mi+1,x),y,z);
19     tr[now]=min(tr[now<<1],tr[now<<1|1])+flag[now];
20 }
21 double que(int now,int l,int r,int x,int y)
22 {
23     if(l==x && r==y) return tr[now];
24     double ret=INF;
25     if(x<=mi) ret=min(ret,que(now<<1,l,mi,x,min(mi,y)));
26     if(y>mi) ret=min(ret,que(now<<1|1,mi+1,r,max(mi+1,x),y));
27     return ret+flag[now];
28 }
29 bool check(double mid)
30 {
31     for(int i=1;i<=n;i++)
32         b[i]=0;
33     for(int i=1;i<=4*n;i++)
34         tr[i]=0,flag[i]=0;
35     for(int i=1;i<=n;i++)
36     {
37         add(1,1,n,i,i,mid*i);
38         add(1,1,n,b[a[i]]+1,i,1);
39         b[a[i]]=i;
40         if(que(1,1,n,1,i)<=mid*(i+1))
41             return 1;
42     }
43     return 0;
44 }
45 int main()
46 {
47     for(scanf("%d",&T);T;T--)
48     {
49         scanf("%d",&n);
50         for(int i=1;i<=n;i++)
51             scanf("%d",&a[i]);
52         double l=0,r=1;
53         for(double mid=(l+r)/2;r-l>eps;mid=(l+r)/2)
54         if(check(mid)) r=mid;else l=mid;
55         printf("%.5f",l);
56     }
57     return 0;
58 }

 

posted @ 2017-08-04 15:16  汪立超  阅读(209)  评论(0编辑  收藏  举报