HDU6070(二分+线段树区间更新)

时间:2021-04-03 04:15:49

Dirt Ratio

Time Limit: 18000/9000 MS (Java/Others) Memory Limit: 524288/524288 K (Java/Others)
Total Submission(s): 1171 Accepted Submission(s): 519
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”.

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

Sample Output
0.5000000000
Hint

For every problem, you can assume its final submission is accepted.

Source
2017 Multi-University Training Contest - Team 4

题意:给你一个数组,定义一个区间的价值为这个区间中出现数的个数除以这个区间的长度。让你从这个序列中找出区间价值最小的个。
解题思路:首先我们二分答案,然后judge函数怎样写呢?,也就是每次要找出一个区间,使得区间的价值小于mid,如下图(盗用的题解的官方图)
HDU6070(二分+线段树区间更新)

这个题解说的很简单,让我来为大家详细分析一下,怎样用线段树来解决这个问题,线段树到底要维护一个什么东西。
显而易见我们每次枚举r从左到右作为区间的右端点,我们维护每个点的一个mid * l + size(l, r)的最小值,易得每个点的mid * l值不变,size(l, r)的值会随着r变化而变化,也就是每次我们考虑当前的点作为r,那么我们我们可以分析出这个a[r]值对前面区间的影响,举个简单的例子,比如前面的序列为1 2 3 4 我们考虑当前的r = 5,a[r] = 3,那么对于区间[1, 5], [2,5],[3,5]的size值不变,因为他们都在更新a[5] = 3之前就包括了a[3] = 3,但是对于区间[4,5]的size值增加了1,因为这个区间之前没有出现过3,所以区间[4,5]整体最小值要加1.

#include <bits/stdc++.h>
using namespace std;
const int maxn = 60000 + 10;
double inf = 100000000000.0;
double eps = 0.00000000000001;
double EPS = 0.00001;
int n;
int a[maxn];
int last[maxn];
struct node{
int l, r;
double Min;
double add;
}Node[maxn<<2];
void pushUp(int i)
{
int lson = i<<1;
int rson = lson|1;
Node[i].Min = min(Node[lson].Min, Node[rson].Min);
}
void pushDown(int i)
{
int lson = i<<1;
int rson = lson|1;
double add = Node[i].add;
if(Node[lson].l == Node[lson].r)
{
if(Node[lson].Min - inf > eps)
{
Node[lson].add = 0;
Node[lson].Min = add;
}
else
{
Node[lson].add = 0;
Node[lson].Min += add;
}
}
else
{
Node[lson].Min = Node[lson].Min + add;
Node[lson].add += add;
}
if(Node[rson].l == Node[rson].r)
{
if(Node[rson].Min - inf > eps)
{
Node[rson].add = 0;
Node[rson].Min = add;
}
else
{
Node[rson].add = 0;
Node[rson].Min += add;
}
}
else
{
Node[rson].Min = Node[rson].Min + add;
Node[rson].add += add;
}
Node[i].add = 0;
}
void build(int i, int l, int r)
{
Node[i].l = l;
Node[i].r = r;
Node[i].Min = inf + 1.0;
Node[i].add = 0;
if(l == r) return;
int mid = (l + r)>>1;
i <<= 1;
build(i, l, mid);
build(i|1, mid + 1, r);
}
void update(int i, int l, int r, double value)
{
if(Node[i].l == l && Node[i].r == r)
{
if(l == r)
{
Node[i].add = 0;
if(Node[i].Min - inf > eps)
{
Node[i].Min = value;
}
else Node[i].Min += value;
return;
}
Node[i].add += value;
Node[i].Min += value;
return;
}
if(fabs(Node[i].add) > eps) pushDown(i);
int f = i;
i <<= 1;
if(r <= Node[i].r) update(i, l, r, value);
else if(l >= Node[i|1].l) update(i|1, l, r, value);
else
{
update(i, l, Node[i].r, value);
update(i|1, Node[i|1].l, r, value);
}
pushUp(f);
}
double query(int i, int l, int r)
{
if(Node[i].l == l && Node[i].r == r)
{
return Node[i].Min;
}
if(fabs(Node[i].add) > eps) pushDown(i);
i <<= 1;
if(r <= Node[i].r) return query(i, l, r);
else if(l >= Node[i|1].l) return query(i|1, l, r);
else
{
return min(query(i, l, Node[i].r), query(i|1, Node[i|1].l, r));
}
}
bool judge(double mid)
{
build(1, 1, n);
memset(last, 0, sizeof(last));
for(int i = 1; i <= n; i++)//枚举r
{
int now = last[a[i]] + 1;
last[a[i]] = i;
update(1, now, i, (double)1);
update(1, i, i, mid * (double)i);
double Min = query(1, 1, n);
double ans = mid * ((double)i + 1.0);
if(Min - ans <= eps) return true;
}
return false;
}
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
for(int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
}
double l = 0;
double r = 1.0;
while(fabs(l - r) > EPS)
{
double mid = (l + r) / 2.0;
if(judge(mid))
{
r = mid;
}
else l = mid;
}
printf("%.10lf\n", l);
}
return 0;
}