zoukankan      html  css  js  c++  java
  • BZOJ1805: [Ioi2007]Sail 船帆 [思维题,线段树优化贪心]

    SailSail

    题目描述见链接 .


    color{red}{正解部分}

    首先旗杆的顺序是对答案没有影响的, 我们只需关注每一行放置了多少旗帜,
    于是可以先按照旗杆的高度排序, 然后考虑从左向右按顺序安插旗子,
    对当前的旗杆 ii, 为了使得它对答案贡献最小, 贪心 地选取 [1,Hi][1, H_i] 高度内旗子个数前 KiK_i 小的高度放置旗子,
    这样能保证答案最优 .

    :证明:
    为了保证答案最优, 放旗子少的高度在后面肯定会放置旗子, 而先放置和后放置对答案是不造成影响的 .

    题目就转化为了: 给出一个数组 A[]A[], 有 NN 次操作, 第 ii 次操作是将 A[]A[] 的前 HiH_i 项的前 KiK_i 小项加 11 .

    可以证明保持 A[]A[] 单调递减可以得到最优方案,

    :证明:
    假设已有一个最优方案, 对于 H1>H2H_1 > H_2A[H1]>A[H2]A[H_1] > A[H_2] 的情况, 可以将 H1H_1 的旗子数与 H2H_2 的旗子数反转, 从而保证 A[]A[] 的递减性 .

    所以要进行操作的元素就可以转化成一个连续的区间: [HiKi+1,Hi][H_i-K_i+1, H_i], 直接使用 线段树 进行区间加减 ??

    仍然不行, 设 L=HiKi+1,R=HiL = H_i-K_i+1, R = H_i, 若 A[L1]==A[L]A[L-1] == A[L], 直接进行区间加会破坏单调性, 于是需要找到 A[j]==A[L]A[j] == A[L] 的最小 jj, 和 A[k]==A[L]A[k] == A[L] 的最大 kk .

    • kRk geq R, 则直接对 区间[j,j+Ki1][j, j+K_i-1] 进行操作 .
    • k<Rk < R, 则分别对区间 [j,j+(Rk)], [k+1,R][j, j+(R-k)], [k+1, R] 进行操作 .

    color{red}{实现部分}

    • LL 时先找到 A[Hi]A[H_i], 然后在线段树查询时尽量往左子树 """靠",
      具体地说, 若左子树的最小值小于等于 A[Hi]A[H_i], 说明左子树可能存在 A[Hi]A[H_i], 否则向右走 .
    • RR 时先找到 A[Hi]A[H_i], 然后在线段树查询时尽量往右子树 """靠",
      具体地说, 若右子树的最大值小于等于 A[Hi]A[H_i], 说明右子树可能存在 A[Hi]A[H_i], 否则向左走 .
    #include<bits/stdc++.h>
    #define reg register
    typedef long long ll;
    
    const int maxn = 100005;
    
    int read(){
            char c;
            int s = 0, flag = 1;
            while((c=getchar()) && !isdigit(c))
                    if(c == '-'){ flag = -1, c = getchar(); break ; }
            while(isdigit(c)) s = s*10 + c-'0', c = getchar();
            return s * flag;
    }
    
    int N;
    int Max_h;
    
    struct Mast{ int h, k; } A[maxn];
    
    bool cmp_1(Mast a, Mast b){ return a.h < b.h; }
    
    struct Seg_tree{ 
            struct Node{ int l, r, max_v, min_v, tag; } T[maxn<<2];
            void Build(int k, int l, int r){
                    T[k].l = l, T[k].r = r;
                    if(l == r) return ; int mid = l+r >> 1;
                    Build(k<<1, l, mid), Build(k<<1|1, mid+1, r);
            }
            void Push_down(int k){
                    T[k<<1].tag += T[k].tag, T[k<<1].min_v += T[k].tag, T[k<<1].max_v += T[k].tag;
                    T[k<<1|1].tag += T[k].tag, T[k<<1|1].min_v += T[k].tag, T[k<<1|1].max_v += T[k].tag;
                    T[k].tag = 0;
            }
            int Query_val(int k, int aim_id){
                    int l = T[k].l, r = T[k].r; 
                    if(l == r) return T[k].min_v; if(T[k].tag) Push_down(k);
                    int mid = l+r >> 1;
                    if(aim_id <= mid) return Query_val(k<<1, aim_id);
                    return Query_val(k<<1|1, aim_id);
            }
            int Query_L(int k, int aim_v){
                    int l = T[k].l, r = T[k].r;
                    if(l == r) return l; if(T[k].tag) Push_down(k);
                    if(T[k<<1].min_v <= aim_v) return Query_L(k<<1, aim_v);
                    return Query_L(k<<1|1, aim_v);
            }
            int Query_R(int k, int aim_v){
                    int l = T[k].l, r = T[k].r;
                    if(l == r) return l; if(T[k].tag) Push_down(k);
                    if(T[k<<1|1].max_v >= aim_v) return Query_R(k<<1|1, aim_v);
                    return Query_R(k<<1, aim_v);
            }
            void Modify(int k, int ql, int qr, int add){
                    int l = T[k].l, r = T[k].r;
                    if(ql <= l && r <= qr){ T[k].tag += add, T[k].max_v += add, T[k].min_v += add; return ; }
                    if(T[k].tag) Push_down(k);
                    int mid = l+r >> 1;
                    if(ql <= mid) Modify(k<<1, ql, qr, add);
                    if(qr > mid) Modify(k<<1|1, ql, qr, add);
                    T[k].min_v = std::min(T[k<<1].min_v, T[k<<1|1].min_v);
                    T[k].max_v = std::max(T[k<<1].max_v, T[k<<1|1].max_v);
            }
            ll calc_ans(int k){
                    int l = T[k].l, r = T[k].r;
                    if(l == r) return 1ll*T[k].min_v*(T[k].min_v-1)/2;
                    if(T[k].tag) Push_down(k);
                    return calc_ans(k<<1) + calc_ans(k<<1|1);
            }
    } seg_t;
    
    int main(){
            N = read();
            for(reg int i = 1; i <= N; i ++) Max_h = std::max(Max_h, A[i].h=read()), A[i].k = read();
            std::sort(A+1, A+N+1, cmp_1);
            seg_t.Build(1, 1, Max_h);
            for(reg int i = 1; i <= N; i ++){
                    int tmp = seg_t.Query_val(1, A[i].h-A[i].k+1);
                    int L = seg_t.Query_L(1, tmp), R = seg_t.Query_R(1, tmp);
                    if(R >= A[i].h) seg_t.Modify(1, L, L+A[i].k-1, 1);
                    else{
                            int len = A[i].k - (A[i].h - R);
                            seg_t.Modify(1, R+1, A[i].h, 1), seg_t.Modify(1, L, L+len-1, 1);
                    }
            }
            printf("%lld
    ", seg_t.calc_ans(1));
            return 0;
    }
    /*
    1. calc_ans() leak of push_down;
    2. Query_val() swap(mid, aim_v)
    */
    

    官方题解

  • 相关阅读:
    C# 数据库连接字符串拼接
    C# 线程同步计数存在的问题
    字符串操作
    字符串位置
    6个基本函数
    占位符
    str转换成int
    python运算符6
    python运算符5
    python运算符4
  • 原文地址:https://www.cnblogs.com/zbr162/p/11822471.html
Copyright © 2011-2022 走看看