HDU 6070 Dirt Ratio(分数规划+线段树)

本文介绍了一道分数规划问题的解题思路,利用二分查找结合线段树实现区间查询,以找到使区间内不同数值比例最小的解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 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\n",ans);
125     }
126     return 0;
127 }

转载于:https://www.cnblogs.com/zyb993963526/p/7283623.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值