HDU 6070 Dirt Ratio 线段树 + 二分

来源:互联网 发布:淘宝积分在哪里查看 编辑:程序博客网 时间:2024/05/29 00:34

传送门:HDU6070

题意:给出一段序列,找出其中一段子序列,使得子序列中不同元素个数除以子序列元素总数最大。

思路:

先贴上官方题解:

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

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

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

看官方题解大体思路能确定下来了,就是二分 + 线段树check,但是怎么在枚举右端点的时候维护这个最小值还不是很明了,其实这个线段树的用法和前几天的codeforce 834D 很相似,每次check重新建树,然后右端点每右移一次,就将该位置的数前一次出现的位置 + 1 到该位置这一段区间在线段树上更新(因为出现了新的元素),线段树每个叶子节点保存的是选当前结点当左端点产生的贡献(也就是上面红字公式的左半边)。

代码:

#include<bits/stdc++.h>#define ll long long#define pb push_back#define fi first#define se second#define pi acos(-1)#define inf 0x3f3f3f3f#define lson l,mid,rt<<1#define rson mid+1,r,rt<<1|1#define rep(i,x,n) for(int i=x;i<n;i++)#define per(i,n,x) for(int i=n;i>=x;i--)using namespace std;typedef pair<int,int>P;const int MAXN=100010;const double eps = 1e-8;double tree[MAXN << 2];int lazy[MAXN << 2];int a[MAXN];int last[MAXN], pre[MAXN];void push_up(int rt){tree[rt] = min(tree[rt << 1], tree[rt << 1 | 1]);}void push_down(int rt){if(!lazy[rt]) return ;lazy[rt << 1] += lazy[rt];lazy[rt << 1 | 1] += lazy[rt];tree[rt << 1] += lazy[rt];tree[rt << 1 | 1] += lazy[rt];lazy[rt] = 0;}void build(double c, int l, int  r, int rt){tree[rt] = lazy[rt] = 0;if(l == r){tree[rt] = c * l;return ;}int mid = (l + r) >> 1;build(c, lson);build(c, rson);push_up(rt);}void update(int L, int R, int l, int r, int rt){if(L <= l && r <= R){lazy[rt]++;tree[rt] += 1;return ;}push_down(rt);int mid = (l + r) >> 1;if(L <= mid)update(L, R, lson);if(R > mid)update(L, R, rson);push_up(rt);}double query(int L, int R, int l, int r, int rt){if(L <= l && r <= R){return tree[rt];}push_down(rt);int mid = (l + r) >> 1;double ans = inf;if(L <= mid)ans = min(ans, query(L, R, lson));if(R > mid)ans = min(ans, query(L, R, rson));return ans;}bool check(double mid, int n){build(mid, 1, n, 1);double tmp;for(int i = 1; i <= n; i++){update(pre[i] + 1, i, 1, n, 1);tmp = query(1, i, 1, n, 1);if(tmp < mid * (i + 1) + eps) return 1;}return 0;}int main(){int T, n;cin >> T;while(T--){cin >> n;memset(last, 0, sizeof(last));for(int i = 1; i <= n; i++){scanf("%d", a + i);pre[i] = last[a[i]];last[a[i]] = i;}double l = 0, r = 1, mid;for(int i = 0; i < 20; i++){mid = (l + r) / 2;if(check(mid, n))r = mid;elsel = mid;}printf("%.5lf\n", mid);} return 0;}