zoukankan      html  css  js  c++  java
  • [HNOI 2017]影魔

    Description

    题库链接

    给你一段长度为 (n) 的序列 (K)(m) 组询问,每次给定左右端点 (l,r) 。求出满足区间内下述贡献和。

    1. 如果一个区间的两个端点是这一个区间的最大与次大值,那么将获得 (p_1) 的价值;
    2. 如果一个区间的一个端点是最大值,而另一个端点不是次大值,那么将获得 (p_2) 的价值。

    (1leq n,mleq 200000)

    Solution

    显然,两种情况都需要满足其中一个端点是最大值。我们可以用单调栈预处理出两个数组 (l_i,r_i) 分别表示左边第一个比 (K_i) 大的数的位置,以及右边第一个比 (K_i) 大的数的位置。

    显然我们枚举位置 (i) 时,满足:

    1. 左端点为 (l_i) 右端点为 (r_i) 时,这个区间贡献为 (p_1)
    2. 左端点为 (l_i) 右端点在 ((i,r_i)) 之间时,贡献为 (p_2)
    3. 左端点在 ((l_i, i)) 之间时,右端点为 (r_i) ,贡献为 (p_2)

    然后就是扫描线来处理所有询问了。

    因为单调队列的 (while) 写成 (if) 调了一下午。

    Code

    //It is made by Awson on 2018.3.6
    #include <bits/stdc++.h>
    #define LL long long
    using namespace std;
    const int N = 200000;
    
    int n, m, p1, p2, a[N+5], l[N+5], r[N+5], S[N+5], top, cnt; LL ans[N+5];
    struct Segment_tree {
    #define lr(o) (o<<1)
    #define rr(o) (o<<1|1)
      LL key[(N<<2)+5], lazy[(N<<2)+5];
      void pushdown(int o, int l, int r, int mid) {
        key[lr(o)] += 1ll*(mid-l+1)*lazy[o];
        key[rr(o)] += 1ll*(r-mid)*lazy[o];
        lazy[lr(o)] += lazy[o], lazy[rr(o)] += lazy[o];
        lazy[o] = 0;
      }
      void update(int o, int l, int r, int a, int b, int k) {
        if (a <= l && r <= b) {key[o] += 1ll*(r-l+1)*k, lazy[o] += k; return; }
        int mid = (l+r)>>1; if (lazy[o]) pushdown(o, l, r, mid);
        if (a <= mid) update(lr(o), l, mid, a, b, k);
        if (b > mid) update(rr(o), mid+1, r, a, b, k);
        key[o] = key[lr(o)]+key[rr(o)];
      }
      LL query(int o, int l, int r, int a, int b) {
        if (a <= l && r <= b) return key[o]; int mid = (l+r)>>1;
        if (lazy[o]) pushdown(o, l, r, mid); LL c1 = 0, c2 = 0;
        if (a <= mid) c1 = query(lr(o), l, mid, a, b);
        if (b > mid) c2 = query(rr(o), mid+1, r, a, b);
        return c1+c2;
      }
    }T;
    struct opts {
      int l, r, t, id, p;
      bool operator < (const opts &b) const {return t < b.t; }
    }s1[N*2+5], s2[N*3+5];
    
    void work() {
      scanf("%d%d%d%d", &n, &m, &p1, &p2);
      for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
      for (int i = 1; i <= m; i++) {
        int l, r;
        scanf("%d%d", &l, &r); ans[i] = 1ll*(r-l)*p1;
        s1[i].l = l, s1[i].r = r, s1[i].t = l-1, s1[i].id = i, s1[i].p = -1;
        s1[i+m].l = l, s1[i+m].r = r, s1[i+m].t = r, s1[i+m].id = i, s1[i+m].p = 1;
      }
      top = 0;
      for (int i = 1; i <= n; i++) {
        while (top > 0 && a[i] > a[S[top]]) --top;
        l[i] = (top == 0 ? 0 : S[top]); S[++top] = i;
      }
      top = 0;
      for (int i = n; i >= 1; i--) {
        while (top > 0 && a[i] > a[S[top]]) --top;
        r[i] = top == 0 ? n+1 : S[top]; S[++top] = i;
      }
      for (int i = 1; i <= n; i++) {
        if (l[i] != 0 && r[i] != n+1) s2[++cnt].l = s2[cnt].r = r[i], s2[cnt].t = l[i], s2[cnt].p = p1;
        if (l[i] != 0 && r[i] > i+1) s2[++cnt].l = i+1, s2[cnt].r = r[i]-1, s2[cnt].t = l[i], s2[cnt].p = p2;
        if (l[i] < i-1 && r[i] != n+1) s2[++cnt].l = l[i]+1, s2[cnt].r = i-1, s2[cnt].t = r[i], s2[cnt].p = p2;
      }
      sort(s1+1, s1+2*m+1); sort(s2+1, s2+cnt+1);
      int n1 = 1, n2 = 1;
      while (n1 <= 2*m) {
        while (n2 <= cnt && s2[n2].t <= s1[n1].t) T.update(1, 1, n, s2[n2].l, s2[n2].r, s2[n2].p), ++n2;
        while (n1 <= 2*m && (s1[n1].t < s2[n2].t || n2 > cnt)) ans[s1[n1].id] += 1ll*T.query(1, 1, n, s1[n1].l, s1[n1].r)*s1[n1].p, ++n1;
      }
      for (int i = 1; i <= m; i++) printf("%lld
    ", ans[i]);
    }
    int main() {
      work(); return 0;
    }
  • 相关阅读:
    LAMP课程(3)
    LAMP课程
    vim文本编辑
    mysql常用语句
    mysql双机互相备份
    Java NIO
    适配器模式
    对象的序列化与反序列化
    字符流
    Java Socket
  • 原文地址:https://www.cnblogs.com/NaVi-Awson/p/8514775.html
Copyright © 2011-2022 走看看