【二分】【线段树】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\n",l); } return 0; }
——The Solution By AutSky_JadeK From UESTC
转载请注明出处:http://www.cnblogs.com/autsky-jadek/