题
OvO http://acm.hdu.edu.cn/showproblem.php?pid=6070
(2017 Multi-University Training Contest - Team 4 - 1004)
解
二分答案
check时,要满足distinct(l,r)/(r-l+1)<val ,将这个不等式转化为distinct(l,r)+val*l<val*(r+1)
check的时候,从左到右枚举右端点r,用线段树维护查询从1到r中选一个l,distinct(l,r)+val*l的最小值
lst数组的作用:lst[i]表示枚举右端点时上一个值为i的点出现的位置
这样的话,线段树的更新就是当枚举到右端点为i的时候,lst[s[i]]+1到i的值全部加1
查询就是查询1到i中的最小值
(思路来源 标程之类的东西)
#include <iostream> #include <cstring> #include <cstdio> #include <cmath> #include <algorithm> using namespace std; const int M=6e4+55; int n; int s[M],lst[M]; double tree[M*3],tag[M*3]; void build(int rt,int li,int ri,double val) { tag[rt]=0; if(li==ri) { tree[rt]=val*li; return ; } int mid=(li+ri)>>1,lc=(rt<<1),rc=(rt<<1)+1; build(lc,li,mid,val); build(rc,mid+1,ri,val); tree[rt]=min(tree[lc],tree[rc]); } void pushdown(int rt,int li,int ri) { if(li==ri) { tag[rt]=0; return ; } int mid=(li+ri)>>1,lc=(rt<<1),rc=(rt<<1)+1; tree[lc]+=tag[rt]; tree[rc]+=tag[rt]; tag[lc]+=tag[rt]; tag[rc]+=tag[rt]; tag[rt]=0; } void update(int rt,int li,int ri,int lq,int rq,double val) //add { if(lq<=li && ri<=rq) { tag[rt]+=val; tree[rt]+=val; return ; } int mid=(li+ri)>>1,lc=(rt<<1),rc=(rt<<1)+1; if(tag[rt]) pushdown(rt,li,ri); if(mid>=lq) update(lc,li,mid,lq,rq,val); if(mid+1<=rq) update(rc,mid+1,ri,lq,rq,val); tree[rt]=min(tree[lc],tree[rc]); } double query(int rt,int li,int ri,int lq,int rq) //get min { double ret=1e9+7; if(lq<=li && ri<=rq) return tree[rt]; int mid=(li+ri)>>1,lc=(rt<<1),rc=(rt<<1)+1; if(tag[rt]) pushdown(rt,li,ri); if(mid>=lq) ret=min(ret,query(lc,li,mid,lq,rq)); if(mid+1<=rq) ret=min(ret,query(rc,mid+1,ri,lq,rq)); return ret; } bool check(double val) { int i,j; double tmp; build(1,1,n,val); memset(lst,0,sizeof(lst)); for(i=1;i<=n;i++) { update(1,1,n,lst[s[i]]+1,i,1); tmp=query(1,1,n,1,i); //tmp=distinct(l,r)+val*l if(tmp<val*(i+1)) //distinct(l,r)+val*l<val*(r+1) => distinct(l,r)/(r-l+1)<val return true; lst[s[i]]=i; } return false; } void solve() { double li=0,ri=1,mid; int i,j,cnt=20; while(cnt--) { mid=(li+ri)/2; if(check(mid)) ri=mid; else li=mid; } printf("%.10lf ",ri); } int main() { int i,j; int cas; cin>>cas; while(cas--) { scanf("%d",&n); for(i=1;i<=n;i++) scanf("%d",&s[i]); solve(); } return 0; }