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

    题意:https://loj.ac/problem/2019

    solution1:

    首先要看清楚题意,两种情况不是互补的。

    (1:forall sin[i+1,j-1],A_s<Ai,A_s<A_j)时产生(p_1)的贡献

    (2:A_i<max{A[i+1...j-1]}<A_j)或者(A_j<max{A[i+1...j-1]}<A_i)时产生(p_2)的贡献

    (3:A_i<max{A[i+1...j-1]},A_j<max{A[i+1...j-1]})不产生贡献

    (4:A_i)形成排列,不存在相等关系。

    每个产生贡献的点对((i,j))依赖(max_{k=i+1}^{j-1}A_k),因此考虑算每个(A_k)的贡献。

    考虑固定每个(A_i)为最大值,设在(i)之前离(i)最近的大于(A_i)的为(A_j)(i)之后离(i)最近的大于(A_i)的为(A_k)

    注意,考虑前后相邻的大于(A_i)的点的原因是如果左右端点越过(j)或者(k),明显(A_i)不再会作为最大值

    因此不应该越界,又由前面的整理,可以得到(A[j+1...i-1]<A_i),(A[i+1...k-1]<A_i)

    产生(p_1)贡献的点对:有且仅有((j,k))

    产生(p_2)贡献的点对:固定(j)为左端点,且右端点在([i+1,k-1])内;固定(k)为右端点,且左端点在([j+1,i-1])

    然后考虑怎么维护。

    首先可以把询问拆成两个前缀相减,然后把贡献和询问都离线掉

    扫到一个询问先把pos(leq)当前询问位置的贡献加上,然后根据是询问的左端点还是右端点维护答案。

    这样对于一个询问来说如果贡献越界,那么就会出现在两次询问里消掉

    #include<cstdio>
    #include<cstring>
    #define R register
    #include<algorithm>
    const int N = 2e5+7;
    typedef long long LL;
    #define debug printf("GG
    ")
    LL ans[N], A[N];
    struct QS {
      int x, id;
    } q[N*2];
    struct Point {
      int pos, l, r, w;
    } p[N*2];
    int st[N], top, tot, vis[N];
    struct BIT {
      LL c[N*2][2];int n;
      #define lbt(x) (x & (-x))
      inline void update(int x, int type, LL k) {
        while (x <= n) c[x][type] += k, x += lbt(x);
      }
      inline LL ask(int x, int type) {
        LL res = 0;
        while (x) res += c[x][type], x -= lbt(x);
        return res;
      }
      inline void Modify(int l, int r, int w) {
        update(l, 0, 1LL * w), update(r + 1, 0, (LL)-w), 
        update(l, 1, 1LL * l * w), update(r + 1, 1, 1LL * (r + 1) * -w);
      }
      inline LL query(int l, int r) {
        return (LL)(((LL)r + 1LL) * ask(r, 0) - ask(r, 1))
          - (LL)((LL)l * ask(l - 1, 0) - ask(l - 1, 1));
      }
    }t;
    inline int cmp1(QS a, QS b) {
      return a.x < b.x;
    }
    inline int cmp2(Point a, Point b) {
      return a.pos < b.pos;
    }
    int n, m, p1, p2, nxt[N], pre[N], L[N], RR[N];
    void findpn() {
      for (R int i = 1; i <= n; i++) {
        while (top && A[st[top]] < A[i]) nxt[st[top--]] = i;
        pre[i] = st[top], st[++top] = i;
      }
      for (R int i = 1; i <= n; i++)
        if (!nxt[i]) nxt[i] = n + 1;
      for (R int i = 1; i <= n; i++) {
        int pr = pre[i], nx = nxt[i];
        if (pr > 0 && nx <= n) p[++tot] = (Point){pr, nx, nx, p1};
        if (pr > 0 && i + 1 <= nx - 1) p[++tot] = (Point){pr, i + 1, nx - 1, p2};
        if (nx <= n && i - 1 >= pr + 1) p[++tot] = (Point){nx, pr + 1, i - 1, p2};
      }
      //debug;
      std :: sort(p + 1, p + tot + 1, cmp2);
    }
    int main() {
      scanf("%d%d%d%d", &n, &m, &p1, &p2);
      t.n = n;
      for (R int i = 1; i <= n; i++)
        scanf("%d", &A[i]);
      for (R int i = 1; i <= m; i++) {
        scanf("%d%d", &L[i], &RR[i]);
        if (L[i] == RR[i]) continue;
        q[(i - 1) * 2 + 1] = (QS){L[i] - 1, i}, q[i * 2] = (QS){RR[i], i};
      } 
      std :: sort(q + 1, q + m * 2 + 1, cmp1);
      findpn();
      int pos = 1, cnt = 1, up = 2 * m;
      for (R int i = 1; i <= up; i++) {
        while (cnt <= q[i].x) 
          t.Modify(cnt + 1, cnt + 1, (LL)p1), cnt++;
        while (pos <= tot && p[pos].pos <= q[i].x) 
          t.Modify(p[pos].l, p[pos].r, (LL)p[pos].w), pos++;
        if (!vis[q[i].id]) 
          ans[q[i].id] = -t.query(L[q[i].id], RR[q[i].id]), vis[q[i].id] = 1;
        else ans[q[i].id] += t.query(L[q[i].id], RR[q[i].id]);
      }
      for (R int i = 1; i <= m; i++)
        printf("%lld
    ", ans[i]);
    return 0;
    }
    
  • 相关阅读:
    复选框和文字对齐解决方案
    封装getElementsByClassName()
    原生js运动框架
    如何防止鼠标移出移入子元素触发mouseout和mouseover事件
    回调函数
    经验
    sublime text3函数追踪:ctags配置
    笔记
    编程经验NO.1 from月光博客
    概念
  • 原文地址:https://www.cnblogs.com/cjc030205/p/11638111.html
Copyright © 2011-2022 走看看