码迷,mamicode.com
首页 > 其他好文 > 详细

[HNOI 2017]影魔

时间:2018-03-06 17:02:31      阅读:154      评论:0      收藏:0      [点我收藏+]

标签:需要   while   esc   预处理   之间   script   math   upd   div   

Description

题库链接

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

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

\(1\leq n,m\leq 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\n", ans[i]);
}
int main() {
  work(); return 0;
}

[HNOI 2017]影魔

标签:需要   while   esc   预处理   之间   script   math   upd   div   

原文地址:https://www.cnblogs.com/NaVi-Awson/p/8514775.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!