【二分】【线段树】hdu6070 Dirt Ratio

size(l,r)表示区间l,r权值的种类数,让你求min{size(l,r)/(r-l+1)}(1<=l<=r<=n)。

last[r]表示a[r]上一次出现的位置,

就是二分验证mid的时候,先把线段树的每个位置i置成mid*i,然后再枚举右端点r,对[last[r]+1,r]作区间+1,然后check此时的前缀最小值是否小于等于mid*(r+1)即可。

据说这个是“01分数规划”?哪位dalao求解释一下……(我 01分数规划 只做过 最优比率生成树……)

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define lson rt<<1,l,m
#define rson rt<<1|1,m+1,r
const double EPS = 0.0000001;
int T;
int n,a[60010],last[60010],now[60010];
double minv[60010<<2];
double delta[60010<<2];
void pushdown(int rt)
{
    if(delta[rt])
      {
        delta[rt<<1]+=delta[rt];
        delta[rt<<1|1]+=delta[rt];
        minv[rt<<1]+=delta[rt];
        minv[rt<<1|1]+=delta[rt];
        delta[rt]=0;
      }
}
void update(int ql,int qr,double v,int rt,int l,int r)
{
    if(ql<=l&&r<=qr)
      {
        delta[rt]+=v;
        minv[rt]+=v;
        return;
      }
    pushdown(rt);
    int m=(l+r)>>1;
    if(ql<=m) update(ql,qr,v,lson);
    if(m<qr) update(ql,qr,v,rson);
    minv[rt]=min(minv[rt<<1],minv[rt<<1|1]);
}
double query(int ql,int qr,int rt,int l,int r)
{
    if(ql<=l&&r<=qr)
      return minv[rt];
    pushdown(rt);
    int m=(l+r)>>1;
    double res=2147483647.0;
    if(ql<=m) res=min(res,query(ql,qr,lson));
    if(m<qr) res=min(res,query(ql,qr,rson));
    return res;
}
bool check(double ratio){
	memset(minv,0,sizeof(minv));
	memset(delta,0,sizeof(delta));
	for(int i=1;i<=n;++i){
		update(i,i,ratio*(double)i,1,1,n);
	}
	for(int i=1;i<=n;++i){
		update(last[i]+1,i,1.0,1,1,n);
		if(query(1,i,1,1,n)-ratio*(double)(i+1)<EPS){
			return 1;
		}
	}
	return 0;
}
int main(){
	scanf("%d",&T);
	for(;T;--T){
		memset(now,0,sizeof(now));
		scanf("%d",&n);
		for(int i=1;i<=n;++i){
			scanf("%d",&a[i]);
		}
		for(int i=1;i<=n;++i){
			last[i]=now[a[i]];
			now[a[i]]=i;
		}
		double l=0.0,r=1.0;
		while(r-l>EPS){
			double mid=(l+r)*0.5;
			if(check(mid)){
				r=mid;
			}
			else{
				l=mid+EPS;
			}
		}
		printf("%.10lf
",l);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/autsky-jadek/p/7281988.html