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
X
problems 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''.
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.
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);
}
}