HDU 6070 二分查找 + 线段树 + 枚举
2017-08-04 17:11
316 查看
二分查找 + 线段树 + 枚举
题意:ACM-ICPC比赛中提交题目会有很多不同的情况,现在给出一个队伍的全部提交情况,只有AC和WA,以序列的形式给出,相同的数字最后出现的一次便是AC,定义一个区间的DirtRatio=cnt(AC)/sum(submit)
也就是整个区间的AC 的数量除以总的提交次数,求出最小的比值是多少。
思路:
其实题意说的并不是很清楚,给出的数列范围是1~60000,而其中任意子区间都可以当做完整的题目提交,也就是说,子区间出现的不同的数字总数就是AC数量,区间长度就是总的提交次数。当时理解时想错了,但是幸好有discuss。。。不过也没想到有这么厉害的线段树解法。
60000范围的数列,子区间很多,不能暴力写!而Dirt Ratio的范围是0~1,精度是10−4 ,所以下次遇到这样的题目就可以选择用二分答案了。
定义:size(l,r) 为区间[l,r] 已经AC的题目数量那么就有size(l,r)/(r−l+1)=mid 化简之后就有size(l,r)+mid∗l=mid∗(r+1) 仔细观察发现如果r是固定的对于每一个size(l,r)+mid∗l 来说 其每一个区间l 是变量,而如果用线段树维护l到r区间的size的话l对于子区间来说是固定的,因为只有1~r-1个区间是目标区间,那么线段树维护的就是每一个l到r区间的 size(l,r)+mid∗l ,所以枚举r,当r确定的时候只需要找到线段树中1~r-1的点size(l,r)+mid∗l 最小值,判断mid是否成立即可,枚举r的方法就是每次插入一个新的r,r对size的影响就是以r为右端点的区间如果不存在a[r] 就把size(l,r)值加1,也就是 size(l,r)+mid∗l 加1.
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int maxn = 60005; const int INF = 0x3f3f3f3f; const double eps = 1e-5; int n; int a[maxn],lastAppear[maxn]; struct Node { int l,r; double sum; double lazy; }tree[maxn<<2]; void Push_up(int root) { tree[root].sum = min(tree[root<<1].sum,tree[root<<1|1].sum); } void Push_down(int root) { tree[root<<1].sum += tree[root].lazy; tree[root<<1].lazy += tree[root].lazy; tree[root<<1|1].sum += tree[root].lazy; tree[root<<1|1].lazy += tree[root].lazy; tree[root].lazy = 0; } void build(int root,int l,int r,double v) { tree[root].l = l,tree[root].r = r; tree[root].lazy = 0; if(l == r) { tree[root].sum = (long long)l*v; return ; } int mid = (l+r)>>1; build(root<<1,l,mid,v); build(root<<1|1,mid+1,r,v); Push_up(root); } void UpDate(int L,int R,int root,int v) { if(L <= tree[root].l && tree[root].r <= R) { tree[root].sum += v; tree[root].lazy += v; /*lazy让子区间的sum整体增加了v,所以sum += v*/ return ; } if(tree[root].lazy != 0) Push_down(root); int mid = (tree[root].l + tree[root].r)>>1; if(L <= mid) UpDate(L,R,root<<1,v); if(R > mid) UpDate(L,R,root<<1|1,v); Push_up(root); } double queryMinSum(int L,int R,int root) { if(L <= tree[root].l && tree[root].r <= R) { return tree[root].sum; } if(tree[root].lazy != 0) Push_down(root); double ans = INF; int mid = (tree[root].l + tree[root].r)>>1; if(L <= mid) ans = queryMinSum(L,R,root<<1); if(R > mid) { double temp = queryMinSum(L,R,root<<1|1); if(temp < ans) ans = temp; } return ans; } int judge(double mid) { build(1,1,n,mid); /*比常规建树多了mid,其实是一个完美的预处理*/ for(int i = 1;i <= n; i++) lastAppear[i] = 0; for(int i = 1;i <= n; i++) { UpDate(lastAppear[a[i]]+1,i,1,1); lastAppear[a[i]] = i; if(queryMinSum(1,i,1) <= mid*(i+1)) return true; } return false; } int main(int argc, char const *argv[]) { //freopen("in.txt","r",stdin); int tt; scanf("%d",&tt); while(tt--) { scanf("%d",&n); for(int i = 1;i <= n; i++) scanf("%d",&a[i]); double l = 0,r = 1,mid,ans; while(r - l > eps) { mid = (r+l)/2; if(judge(mid)) r = (ans = mid) - eps; else l = mid + eps; } printf("%.9f\n",ans); } return 0; }
相关文章推荐
- hdu 6070 Dirt Ratio(二分+线段树)(2017 Multi-University Training Contest - Team 4 )
- hdu 6070 枚举答案+线段树
- hdu 6070 Dirt Ratio(二分+线段树)(2017 Multi-University Training Contest - Team 4 )
- 2017 Multi-University Training Contest - Team 4 HDU 6070 Dirt Ratio (二分+ 线段树)
- Hdu 6070 Dirt Ratio【分数规划+二分+线段树】好题~好题~
- HDU 6070 Dirt Ratio(二分+线段树)
- hdu 2141 枚举后 二分查找
- HDU 6070 Dirt Ratio 分数规划 二分 线段树维护区间最值
- HDU - 6070 - Dirt Ratio(二分+线段树)
- hdu 6070 Dirt Ratio(二分+线段树)(2017 Multi-University Training Contest - Team 4 )
- HDU 6070 Dirt Ratio 二分+线段树
- hdu 6070 Dirt Ratio 二分,线段树
- hdu 6070 Dirt Ratio(二分+线段树)(2017 Multi-University Training Contest - Team 4 )
- hdu 6070 Dirt Ratio(二分+线段树)(2017 Multi-University Training Contest - Team 4 )
- hdu 4614 Vases and Flowers(线段树加二分查找)
- HDU 4614 Vases and Flowers (二分查找+线段树区间更新)
- HDU 6070 Dirt Ratio (二分+线段树, 2017 Multi-Univ Training Contest 4)
- hdu-6070(二分+线段树)
- hdu 6070 Dirt Ratio(二分+线段树)(2017 Multi-University Training Contest - Team 4 )
- 【HDU 6070 Dirt Ratio】 二分 & 线段树