比赛中我看了一眼题目就觉得是二分的套路,跟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 }