• 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
    ", r + eps);
    90     }
    91     return 0;
    92 }
    View Code
  • 相关阅读:
    Android研究之游戏开发处理按键的响应
    C语言指针的初始化和赋值
    Cloudera CDH 5集群搭建(yum 方式)
    未将对象引用设置到对象的实例--可能出现的问题总结
    内存泄漏以及常见的解决方法
    都能看懂的嵌入式linux/android alsa_aplay alsa_amixer命令行使用方法
    Life is hard!
    EasyUI基础入门之Resiable(可缩放)
    Android -- Looper.prepare()和Looper.loop() —深入版
    vi 命令 使用方法
  • 原文地址:https://www.cnblogs.com/geloutingyu/p/7298049.html
Copyright © 2020-2023  润新知