HDU 6070-Dirt Ratio(多校训练第四场->二分+线段树)

Dirt Ratio

Time Limit: 18000/9000 MS (Java/Others)    Memory Limit: 524288/524288 K (Java/Others)
Total Submission(s): 1339    Accepted Submission(s): 605
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(1T15) , denoting the number of test cases.

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

In the next line, there are n positive integers a1,a2,...,an(1ain) , 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 104 .
 

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
题意:换一种方式描述:给你长为n的序列,编号相同代表颜色一样,选择一段序列使得颜色数 / 该段总数最小
题解:可以考虑二分,设当前最小值为mid,该段总数为r-l+1,颜色数为size(l,r),则该段的比值需要满足:
size(l,r)/(r−l+1) ≤ mid, 也就是 size(l, r) + mid × l ≤mid × (r + 1)。剩下的问题就很好解决了,直接枚举区间右端点即可, 在每一次枚举中r就是一个常数,首先将r加入到[r,r]的区间内,然后更新r到上一次出现r的位置这一区间,我们可以用线段树维护区间最小值,线段树上的每一个点代表不等式左边的表达式。

#include<map>              
#include<stack>              
#include<queue>            
#include<vector>              
#include<math.h>        
#include<time.h>      
#include<stdio.h>            
#include<iostream>          
#include<string.h>              
#include<stdlib.h>              
#include<algorithm>              
using namespace std;              
typedef long long  ll;              
#define inf 2147483647             
#define mod 998244353          
#define maxn  200005          
#define lowbit(x) (x&-x)              
#define eps 1e-6   
double mins[maxn];
int p[maxn],a[maxn],pos[maxn],n,lazy[maxn];
void pushdown(int id)
{
	if(lazy[id]>eps)
	{
		mins[id*2]+=lazy[id];
		mins[id*2+1]+=lazy[id];
		lazy[id*2]+=lazy[id];
		lazy[id*2+1]+=lazy[id];
		lazy[id]=0;
	}
}
void updata(int id,int l,int r,int a,int b,double x)
{
	if(r<a || l>b)
		return;
	if(l>=a && r<=b)
	{
		mins[id]+=x;
		lazy[id]+=x;
		return;
	}
	pushdown(id);
	int m=(l+r)/2;
	if(a<=m)
		updata(id*2,l,m,a,b,x);
	if(b>m)
		updata(id*2+1,m+1,r,a,b,x);
	mins[id]=min(mins[id*2],mins[id*2+1]);
}
double query(int id,int l,int r,int a,int b)
{
	if(r<a || l>b)
		return 100000000.0;
	if(l>=a && r<=b)
		return mins[id];
	int m=(l+r)/2;
	double res=10000000.0;
	pushdown(id);
	if(a<=m)
		res=min(res,query(id*2,l,m,a,b));
	if(b>m)
		res=min(res,query(id*2+1,m+1,r,a,b));
	mins[id]=min(mins[id*2],mins[id*2+1]);
	return res;
}
int main(void)
{
	int T,i,j;
	double l,r,mid,tmp=0;
	scanf("%d",&T);
	while(T--)
	{
		memset(p,0,sizeof(p));
		memset(pos,0,sizeof(pos));
		double ans=0;
		scanf("%d",&n);
		for(i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			p[i]=pos[a[i]];
			pos[a[i]]=i;
		}
		l=0;r=1.0;
		while(fabs(r-l)>eps)
		{
			memset(mins,0,sizeof(mins));
			memset(lazy,0,sizeof(lazy));
			mid=(l+r)/2.0;bool flag=0;
			for(i=1;i<=n;i++)
			{
				updata(1,1,n,i,i,mid*i);
				updata(1,1,n,p[i]+1,i,1.0);
				tmp=query(1,1,n,1,i);
				if(tmp<=mid*(i+1))
				{
					flag=1;
					break;
				}
			}
			if(flag)
				r=mid,ans=mid;
			else
				l=mid;
		}
		printf("%.10f\n",ans);
	}
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值