http://acm.hdu.edu.cn/showproblem.php?pid=6070
题意:
找出一个区间,使得(区间内不同数的个数/区间长度)的值最小,并输出该值。
思路:
因为是要求$frac{f(x)}{g(x)}$的最值,所以这是分数规划的题目,对于分数规划,是要用二分查找的方式去解决的。
就像官方题解说的,二分查找mid,二分答案mid,检验是否存在一个区间满足$frac{size(l,r)}{(r-l+1)}<=mid$,表示l~r内不同数的个数。
先把上面的式子转化一下,,用线段树维护区间内不同数的个数,因为l*mid是固定值,所以把它也可以加进去,这样线段树就维护了区间内不等式左边的最小值。
从左到右枚举r,先是在pre[a[r]]+1~r这段区间内将区间值+1,因为这段区间内a[r]并没有出现过。更新完了之后就查询,因为线段树内记录的就是不等式左边的最小值,所以就可以返回最小值然后判断是否小于等于(r+l)*mid。
1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 #include<cstdio> 5 #include<sstream> 6 #include<vector> 7 #include<stack> 8 #include<queue> 9 #include<cmath> 10 #include<map> 11 #include<set> 12 using namespace std; 13 typedef long long ll; 14 typedef pair<int,int> pll; 15 const int INF = 0x3f3f3f3f; 16 const int maxn=1e6+5; 17 const int mod=998244353; 18 const double eps=1e-7; 19 20 int n; 21 double now; 22 int a[maxn]; 23 int pre[maxn]; 24 double add[maxn<<4]; 25 double sum[maxn<<4]; 26 27 void PushUp(int o) 28 { 29 sum[o]=min(sum[o<<1],sum[o<<1|1]); 30 } 31 32 void PushDown(int o) 33 { 34 if(add[o]) 35 { 36 add[o<<1]+=add[o]; 37 add[o<<1|1]+=add[o]; 38 sum[o<<1]+=add[o]; 39 sum[o<<1|1]+=add[o]; 40 add[o]=0; 41 } 42 } 43 44 void build(int l, int r, int o) 45 { 46 sum[o]=add[o]=0; 47 if(l==r) 48 { 49 sum[o]=l*now; 50 return ; 51 } 52 int mid=(l+r)>>1; 53 build(l,mid,o<<1); 54 build(mid+1,r,o<<1|1); 55 PushUp(o); 56 } 57 58 void update(int ql, int qr, int l, int r, int x, int o) 59 { 60 if(ql<=l && qr>=r) 61 { 62 sum[o]+=x; 63 add[o]+=x; 64 return; 65 } 66 PushDown(o); 67 int mid=(l+r)>>1; 68 if(mid>=ql) update(ql,qr,l,mid,x,o<<1); 69 if(mid<qr) update(ql,qr,mid+1,r,x,o<<1|1); 70 PushUp(o); 71 } 72 73 double query(int ql, int qr, int l, int r, int o) 74 { 75 if(ql<=l && qr>=r) 76 { 77 return sum[o]; 78 } 79 PushDown(o); 80 double ans=INF; 81 int mid=(l+r)>>1; 82 if(mid>=ql) ans=min(ans,query(ql,qr,l,mid,o<<1)); 83 if(mid<qr) ans=min(ans,query(ql,qr,mid+1,r,o<<1|1)); 84 return ans; 85 } 86 87 bool check() 88 { 89 memset(pre,0,sizeof(pre)); 90 build(1,n,1); 91 for(int i=1;i<=n;i++) 92 { 93 double tmp=now*(i+1.0); 94 update(pre[a[i]]+1,i,1,n,1,1); 95 if(query(1,i,1,n,1)<=tmp) return true; 96 pre[a[i]]=i; 97 } 98 return false; 99 } 100 101 int main() 102 { 103 //freopen("in.txt","r",stdin); 104 int T; 105 scanf("%d",&T); 106 while(T--) 107 { 108 scanf("%d",&n); 109 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 110 111 double l=0,r=1; 112 double ans; 113 while(r-l>=eps) 114 { 115 double mid = (r+l)/2.0; 116 now = mid; 117 if(check()) 118 { 119 ans=mid; 120 r=mid-eps; 121 } 122 else l=mid+eps; 123 } 124 printf("%.9lf ",ans); 125 } 126 return 0; 127 }