2017 多校4 Dirt Ratio

多校4 Dirt Ratio(二分+线段树)

题意:

给出n个数,找一段区间使得区间内不同数字个数除以区间长度最小,求这个最小值,\(n<=60000,a_i<=n\)

题解:

二分答案mid,检验是否存在一个区间满足\(\frac{size(l,r)}{r-l+1}\leq mid ​\),也就是\(size(l,r)+mid\times l\leq mid\times (r+1)\)

从左往右枚举每个位置作\(r\),当rr变化为\(r+1\)时,对\(size\)的影响是一段区间加1,线段树维护区间最小值即可。

时间复杂度\(O(n\log n\log w)\)

二分答案和枚举端点想到了,但是求最小值只会遍历,复杂度太高,没想到还能线段树维护,get新技能了。
从左往右枚举右端点,那么第\(i\)个数的贡献就是\(last[a[i]]+1到i\),第i个数都是有贡献的,然后就用线段树搞搞,这样就降了一维复杂度了。

#include<bits/stdc++.h>
#define LL long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define ls (rt<<1)
#define rs (rt<<1|1)
using namespace std;
const int N = 6e4 + 10;
const double eps = 1e-6;
int read(){
    int x = 0;
    char c = getchar();
    while(c < '0' || c >'9') c = getchar();
    while(c >= '0' && c <= '9') x = x * 10 + c - 48 , c = getchar();
    return x;
}
int n;
int c[N],last[N],pre[N];
double s[N<<2],col[N<<2];
void pushup(int rt){
    s[rt] = min(s[ls],s[rs]);
}
void pushdown(int rt,int m){
    if(col[rt] > eps){
        col[ls] += col[rt];
        col[rs] += col[rt];
        s[ls] += col[rt];
        s[rs] += col[rt];
        col[rt] = 0;
    }
}
void update(int L,int R,double val,int l,int r,int rt){
    if(L <= l && R >= r){
        s[rt] += val;
        col[rt] += val;
        return ;
    }
    pushdown(rt,r - l + 1);
    int m = l + r >> 1;
    if(L <= m) update(L,R,val,lson);
    if(R > m) update(L,R,val,rson);
    pushup(rt);
}
double query(int L,int R,int l,int r,int rt){
    if(L <= l && R >= r){
        return s[rt];
    }
    pushdown(rt,r - l + 1);
    int m = l + r >> 1;
    double ans = 100000000;
    if(L <= m) ans = min(ans,query(L,R,lson));
    if(R > m) ans = min(ans,query(L,R,rson));
    return ans;
}
bool check(double mid){
    memset(s,0,sizeof(s));
    memset(col,0,sizeof(col));
    for(int i = 1;i <= n;i++){
        update(last[i]+1,i,1,1,n,1);
        update(i,i,mid * i,1,n,1);
        double res = query(1,i,1,n,1);
       // printf("%d %.6f\n",i,res);
        if(mid * (i + 1) - res > eps) return true;
    }
    return false;
}
int main(){

    int T;
    T = read();
    while(T--){
      n = read();
      memset(pre,0,sizeof(pre));
      for(int i = 1;i <= n;i++) {
        c[i] = read();
        last[i] = pre[c[i]];
        pre[c[i]] = i;
      }
      double l = 0,r = 1,ans = r;
      while(r - l > eps){
        double mid = (l + r) / 2;
        if(check(mid)) ans = min(ans,mid),r = mid;
        else l = mid;
      }
      printf("%.6lf\n",ans);
    }
    return 0;
}


posted @ 2017-08-04 12:43  jiachinzhao  阅读(171)  评论(0编辑  收藏  举报