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
Sample Output
0.5000000000HintFor every problem, you can assume its final submission is accepted.
题意:要求找一段区间使得区间内不同数的个数/区间长度的比值最小
二分答案+线段树维护
我们先二分答案得到k,那么我们需要在序列中找一段区间使得它的sum/len<=k
用线段树维护sum,我们每插入一个数时先update(1,pre[i]+1,i,1),在update(1,1,i,-k),这样就可以了,最后只要判断区间内是否存在答案为k
的存在就继续二分……
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn = 60000+10;
int T,N;
int a[maxn],pre[maxn],last[maxn];
struct Info{
double sum;
double lazy;
int l,r;
};
struct SGT{
Info tree[maxn<<2];
void pushUp(int now){
tree[now].sum = min(tree[now<<1].sum,tree[now<<1|1].sum);
}
void pushDown(int now){
tree[now<<1].sum += tree[now].lazy;
tree[now<<1|1].sum += tree[now].lazy;
tree[now<<1].lazy += tree[now].lazy;
tree[now<<1|1].lazy += tree[now].lazy;
tree[now].lazy = 0;
}
void build(int now,int l,int r){
tree[now].l = l;
tree[now].r = r;
tree[now].lazy = 0;
if(l == r){
tree[now].sum = 0;
return;
}
int Mid = (l+r)>>1;
build(now<<1,l,Mid);
build(now<<1|1,Mid+1,r);
pushUp(now);
}
void update(int now,int l,int r,double val){
if(l <= tree[now].l && tree[now].r <= r){
tree[now].sum += val;
tree[now].lazy += val;
return;
}
pushDown(now);
int Mid = (tree[now].l + tree[now].r)>>1;
if(l <= Mid)
update(now<<1,l,r,val);
if(Mid < r)
update(now<<1|1,l,r,val);
pushUp(now);
}
double query(int now,int l,int r){
if(l <= tree[now].l && tree[now].r <= r)
return tree[now].sum;
pushDown(now);
double ans = N;
int Mid = (tree[now].l + tree[now].r)>>1;
if(l <= Mid)
ans = min(ans,query(now<<1,l,r));
if(Mid < r)
ans = min(ans,query(now<<1|1,l,r));
pushUp(now);
return ans;
}
}sgt;
bool solve(double k){
sgt.build(1,1,N);
for(int i = 1; i <= N; i++){
sgt.update(1,pre[i]+1,i,1);
sgt.update(1,1,i,-k);
if(sgt.query(1,1,i) <= 0)
return true;
}
return false;
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%d",&N);
for(int i = 1; i <= N; i++)
pre[i] = last[i] = 0;
for(int i = 1; i <= N; i++){
scanf("%d",&a[i]);
pre[i] = last[a[i]];
last[a[i]] = i;
}
double l,r;
l = 0,r = 1;
for(int i = 0; i < 20; i++){
double mid = (l+r)/2;
if(solve(mid))
r = mid;
else
l = mid;
}
printf("%.5f\n",(r+l)/2);
}
return 0;
}