您的位置:首页 > 其它

Hdu 6070 Dirt Ratio【分数规划+二分+线段树】好题~好题~

2017-08-05 15:15 543 查看


Dirt Ratio

Time Limit: 18000/9000 MS (Java/Others)    Memory Limit: 524288/524288 K (Java/Others)

Total Submission(s): 1515    Accepted Submission(s): 698
Special Judge


Problem Description

In ACM/ICPC contest, the ''Dirt Ratio'' of a team is calculated in the following way. First let's ignore all the problems the team didn't pass, assume the team passed Xproblems
during the contest, and submitted Y times
for these problems, then the ''Dirt Ratio'' is measured as XY.
If the ''Dirt Ratio'' of a team is too low, the team tends to cause more penalty, which is not a good performance.



Picture from MyICPC

Little Q is a coach, he is now staring at the submission list of a team. You can assume all the problems occurred in the list was solved by the team during the contest. Little Q calculated the team's low ''Dirt Ratio'', felt very angry. He wants to have a talk
with them. To make the problem more serious, he wants to choose a continuous subsequence of the list, and then calculate the ''Dirt Ratio'' just based on that subsequence.

Please write a program to find such subsequence having the lowest ''Dirt Ratio''.

 

Input

The first line of the input contains an integer T(1≤T≤15),
denoting the number of test cases.

In each test case, there is an integer n(1≤n≤60000) in
the first line, denoting the length of the submission list.

In the next line, there are n positive
integers a1,a2,...,an(1≤ai≤n),
denoting the problem ID of each submission.

 

Output

For each test case, print a single line containing a floating number, denoting the lowest ''Dirt Ratio''. The answer must be printed with an absolute error not greater than 10−4.

 

Sample Input

1
5
1 2 1 2 3

题目大意:

让你找到一个区间:【L,R】,使得其中的数字的种类数/区间长度最小。

问这个比例最小是多少。

思路:

考虑分数规划:

①我们希望Val【L,R】/(R-L+1)最小。这里Val【L,R】表示区间中数字的种类数。

②我们不妨设定F(X)=Val【L,R】-X*(R-L+1);

③那么如果有F(X)<=0,那么一定有Val【L,R】/(R-L+1)<=X;

④所以我们这个最小的值可以通过二分来求出来。

那么我们如何check呢?

我们再转化一下不等式变成:Val【L,R】+mid*L<=(R+1)*mid.(这里X==mid);

那么我们O(n)枚举R,之前我们可以利用线段树来维护mid*L+Val【L,R】的最小值,那么如果查询Query(1,i-1)能够<=(R+1)*mid的话,就说明可以继续向下二分。

否则向上。

我们线段树更新mid*L很简单,一路update(i,i,mid*i)即可,那么Val【L,R】怎样处理呢?其实也不难,我们维护一个数组last【i】,表示数字i出现的最后的位子,那么对于当前数i,其影响的区间Val的值的范围就是从【last【a【i】】+1,i】,那么再update一下(last【a【i】】+1,i,1)即可。

尽量去for二分。

Ac代码:

#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
#define lson l,m,rt*2
#define rson m+1,r,rt*2+1
double tree[350050*8];
double flag[350050*8];
int last[350050];
int a[350050];
void build(int l,int r,int rt){
flag[rt]=0;
if(l==r)
{
tree[rt]=0;
flag[rt]=0;
return;
}
int m=(l+r)>>1;
build(lson);
build(rson);
}
void pushdown(int l,int r,int rt)//向下维护树内数据
{
if(flag[rt])//如果贪婪标记不是0(说明需要向下进行覆盖区间(或点)的值)
{
int m=(l+r)/2;
flag[rt*2]+=flag[rt];
flag[rt*2+1]+=flag[rt];
tree[rt*2]+=flag[rt];//千万理解如何覆盖的区间值(对应线段树的图片理解(m-l)+1)是什么意识.
tree[rt*2+1]+=flag[rt];
flag[rt]=0;
}
}
void pushup(int rt)
{
tree[rt]=max(tree[rt<<1],tree[rt<<1|1]);
}
double Query(int L,int R,int l,int r,int rt)
{
if(L<=l&&r<=R)
{
return tree[rt];
}
else
{
pushdown(l,r,rt);
int m=(l+r)>>1;
double ans=-10000000000000;
if(L<=m)
{
ans=max(ans,Query(L,R,lson));
}
if(m<R)
{
ans=max(ans,Query(L,R,rson));
}
pushup(rt);
return ans;
}
}
void update(int L,int R,double c,int l,int r,int rt)
{
if(L<=l&&r<=R)//覆盖的是区间~
{
tree[rt]+=c;//覆盖当前点的值
flag[rt]+=c;//同时懒惰标记~!
return ;
}
pushdown(l,r,rt);
int m=(l+r)/2;
if(L<=m)
{
update(L,R,c,lson);
}
if(m<R)
{
update(L,R,c,rson);
}
pushup(rt);
}
int Slove(int n,double mid)
{
build(1,n,1);
for(int i=1;i<=n;i++)last[a[i]]=0;
for(int i=1;i<=n;i++)
{
update(i,i,-mid*i,1,n,1);
update(last[a[i]]+1,i,-1,1,n,1);
last[a[i]]=i;
if(i==1)continue;
double Q=-Query(1,i-1,1,n,1);
if(Q<=(i+1)*mid)return 1;
}
return 0;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
double l=0;
double r=1;
double ans;
for(int i=0;i<20;i++)
{
double mid=(l+r)/2;
if(Slove(n,mid)==1)
{
ans=mid;
r=mid;
}
else l=mid;
}
printf("%.10f\n",ans);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Hdu 6070