首页 > 代码库 > hdu6070(分数规划/二分+线段树区间更新,区间最值)

hdu6070(分数规划/二分+线段树区间更新,区间最值)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=6070

 

题意: 给出一个题目提交序列, 从中选出一个正确率最小的子串. 选中的子串中每个题目当且仅当最后一次提交是正确的.

 

思路: 分数规划

二分答案, 然后在 check 函数中查找是否存在某个区j间 [l, r] 使得 sum(l, r) / (r - l + 1) <= mid, 即 sum(l, r) + l * mid <= (r + 1) * mid. 可以用个线段树来维护 sum(l, r) + l * mid . 建树时直接将 l * mid 放入树中, 然后从左到右枚举 r, 对于当前 i, a[i] 对区间 [pre[i] + 1, i] 的贡献为一(区间 [1, pre[i]] 内的贡献之前的a[i]已经计算了) . 这样对于当前更新后, 1 <= j <= i , sum[j] 即为区间 [j, i] 内的贡献. 那么对于当前 i, query(1, i) 就得到了所有以 i 为后缀的区间的贡献最小值. 遍历完 r 后即得到了所有区间的贡献最小值.

最后要注意一下线段树区间更新,区间最值的 lazy 数组维护写法, 最值和区间求和是不同的.

 

代码:

技术分享
 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <string.h>
 4 #define lson l, mid, rt << 1
 5 #define rson mid + 1, r, rt << 1 | 1
 6 using namespace std;
 7 
 8 const double eps = 1e-5;
 9 const int MAXN = 6e4 + 10;
10 int a[MAXN], pre[MAXN], last[MAXN], n;
11 double sum[MAXN << 2], lazy[MAXN << 2];
12 
13 void push_up(int rt){ //向上更新取最值
14     sum[rt] = min(sum[rt << 1], sum[rt << 1 | 1]);
15 }
16 
17 void push_down(int rt){
18     if(lazy[rt]){//将标记向下更新,维护的是最值,sum不需要求和
19         lazy[rt << 1] += lazy[rt];
20         lazy[rt << 1 | 1] += lazy[rt];
21         sum[rt << 1] += lazy[rt];
22         sum[rt << 1 | 1] += lazy[rt];
23         lazy[rt] = 0;
24     }
25 }
26 
27 void build(int l, int r, int rt, double value){
28     lazy[rt] = 0;
29     if(l == r){
30         sum[rt] = value * l;
31         return;
32     }
33     int mid = (l + r) >> 1;
34     build(lson, value);
35     build(rson, value);
36     push_up(rt);
37 }
38 
39 void update(int L, int R, int value, int l, int r, int rt){
40     if(L <= l && R >= r){
41         lazy[rt] += value;
42         sum[rt] += value;//维护的是最值,sum不需要求和
43         return;
44     }
45     push_down(rt);
46     int mid = (l + r) >> 1;
47     if(L <= mid) update(L, R, value, lson);
48     if(R > mid) update(L, R, value, rson);
49     push_up(rt);
50 }
51 
52 double query(int L, int R, int l, int r, int rt){
53     if(L <= r && R >= r) return sum[rt];
54     push_down(rt);
55     double cnt = 1e5;
56     int mid = (l + r) >> 1;
57     if(L <= mid) cnt = min(cnt, query(L, R, lson));
58     if(R > mid) cnt = min(cnt, query(L, R, rson));
59     return cnt;
60 }
61 
62 bool check(double mid){
63     build(1, n, 1, mid);
64     for(int i = 1; i <= n; i++){
65         update(pre[i] + 1, i, 1, 1, n, 1);
66         if(query(1, i, 1, n, 1) <= (double)mid *(i + 1)) return true;
67     }
68     return false;
69 }
70 
71 int main(void){
72     int t;
73     scanf("%d", &t);
74     while(t--){
75         scanf("%d", &n);
76         memset(pre, 0, sizeof(pre));
77         memset(last, 0, sizeof(last));
78         for(int i = 1; i <= n; i++){
79             scanf("%d", &a[i]);
80             pre[i] = last[a[i]];
81             last[a[i]] = i;
82         }
83         double l = 0, r = 1;
84         while(r - l > eps){
85             double mid = (l + r) / 2;
86             if(check(mid)) r = mid - eps;
87             else l = mid + eps;
88         }
89         printf("%.5lf\n", r + eps);
90     }
91     return 0;
92 }
View Code

 

hdu6070(分数规划/二分+线段树区间更新,区间最值)