[BZOJ4826][Hnoi2017]影魔(线段树)

和Hnoi2016的序列差不多,但这道题用莫队会被卡成30分
把问题抽象到 n×n n × n 的二维平面上,二维平面上 (i,j) ( i , j ) 位置的数值表示灵魂对 i,j i , j 提供的攻击力( ij i ≥ j (i,j) ( i , j ) 位置的数值为 0 0 )。
由题意,对于灵魂对i,j(i<j)
1、如果 k[i] k [ i ] k[j] k [ j ] 中一个是区间 [i,j] [ i , j ] 的最大值,另一个是区间 [i,j] [ i , j ] 的次大值,则提供的攻击力为 p1 p 1
2、如果 k[i] k [ i ] k[j] k [ j ] 中一个是区间 [i,j] [ i , j ] 的最大值,另一个不是区间 [i,j] [ i , j ] 的次大值,则提供的攻击力为 p2 p 2
先预处理出 i i 往左第一个比k[i]大的位置 prei p r e i ,和 i i 往右第一个比k[i]大的位置 sufi s u f i
先不考虑另一个端点是否为次大值。易得:
对于任何一个 i i ,左端点在[prei+1,i1],右端点为 i i ,和左端点为i,右端点在 [i+1,sufi1] [ i + 1 , s u f i − 1 ] 的区间的最大值为 k[i] k [ i ]
所以,预处理修改时,将横坐标为 [prei+1,i1] [ p r e i + 1 , i − 1 ] ,纵坐标为 i i 的矩形内的数加上p2,再将横坐标为 i i ,纵坐标为[i+1,sufi1]的矩形内的数也加上 p2 p 2
同时注意到,上面的处理忽略了另一个端点为次大值的情况,这种情况更好处理:
对于任何一个 i i k[i]在区间 [prei,i] [ p r e i , i ] 和区间 [i,sufi] [ i , s u f i ] 为次大值。
也就是将坐标 (prei,i) ( p r e i , i ) (i,sufi) ( i , s u f i ) 两个坐标的数值加上 p1p2 p 1 − p 2
询问就是查询一个矩形内所有数的和。
怎样实现呢?当然是用万能的线段树了!离线排序时候扫描线+线段树解决。
代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define p2 p << 1
#define p3 p << 1 | 1
using namespace std;
inline int read() {
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
typedef long long ll;
const int N = 2e5 + 5, M = 1e6 + 5;
int n, m, P1, P2, a[N], stk[N], top, pre[N], suf[N], tot;
struct cyx {
    int t, l, r, dx, dy; cyx() {}
    cyx(int _t, int _l, int _r, int _dx, int _dy) :
        t(_t), l(_l), r(_r), dx(_dx), dy(_dy) {}
} orz[M];
struct pyz {int l, r, id;} que[N]; ll sumx[M], sumy[M], addx[M], addy[M], ans[N];
inline bool comp1(const cyx &a, const cyx &b) {return a.t > b.t;}
inline bool comp2(const pyz &a, const pyz &b) {return a.l > b.l;}
inline void down(int p) {
    addx[p2] += addx[p]; addx[p3] += addx[p]; addx[p] = 0;
    addy[p2] += addy[p]; addy[p3] += addy[p]; addy[p] = 0;
}
inline void upt(int l, int r, int p) {
    int mid = l + r >> 1; sumx[p] = sumx[p2] + sumx[p3] + addx[p2] *
    (mid - l + 1) + addx[p3] * (r - mid); sumy[p] = sumy[p2] + sumy[p3]
    + addy[p2] * (mid - l + 1) + addy[p3] * (r - mid);
}
inline void change(int l, int r, int s, int e, int dx, int dy, int p) {
    if (l == s && r == e) return (void) (addx[p] += dx, addy[p] += dy);
    int mid = l + r >> 1; down(p);
    if (e <= mid) change(l, mid, s, e, dx, dy, p2);
    else if (s >= mid + 1) change(mid + 1, r, s, e, dx, dy, p3);
    else change(l, mid, s, mid, dx, dy, p2),
        change(mid + 1, r, mid + 1, e, dx, dy, p3); upt(l, r, p);
}
inline ll ask(int l, int r, int s, int e, int x, int p) {
    if (l == s && r == e) return (sumx[p] + addx[p] * (r - l + 1))
        * x + (sumy[p] + addy[p] * (r - l + 1));
    int mid = l + r >> 1; down(p); ll res;
    if (e <= mid) res = ask(l, mid, s, e, x, p2);
    else if (s >= mid + 1) res = ask(mid + 1, r, s, e, x, p3);
    else res = ask(l, mid, s, mid, x, p2) + ask(mid + 1, r, mid + 1, e, x, p3);
    return upt(l, r, p), res;
}
int main() {
    int i, j; n = read(); m = read(); P1 = read(); P2 = read();
    for (i = 1; i <= n; i++) a[i] = read();
    stk[top = 0] = 0; for (i = 1; i <= n; i++) {
        while (top && a[stk[top]] < a[i]) top--;
        pre[stk[++top] = i] = stk[top - 1];
    }
    stk[top = 0] = n + 1; for (i = n; i; i--) {
        while (top && a[stk[top]] < a[i]) top--;
        suf[stk[++top] = i] = stk[top - 1];
    }
    for (i = 1; i <= n; i++) {
        if (i + 1 != suf[i]) orz[++tot] = cyx(i, i + 1, suf[i] - 1, 0, P2);
        if (pre[i]) orz[++tot] = cyx(pre[i], i, i, 0, P1 - P2);
        if (suf[i] <= n) orz[++tot] = cyx(i, suf[i], suf[i], 0, P1 - P2);
        if (pre[i] + 1 != i) {
            orz[++tot] = cyx(i - 1, i, i, -P2, i * P2);
            orz[++tot] = cyx(pre[i], i, i, P2, -(pre[i] + 1) * P2);
        }
    }
    for (i = 1; i <= m; i++) que[i].l = read(), que[i].r = read(), que[i].id = i;
    sort(orz + 1, orz + tot + 1, comp1); sort(que + 1, que + m + 1, comp2);
    for (i = j = 1; i <= m; i++) {
        while (j <= tot && orz[j].t >= que[i].l)
            change(1, n, orz[j].l, orz[j].r, orz[j].dx, orz[j].dy, 1), j++;
        ans[que[i].id] = ask(1, n, que[i].l, que[i].r, que[i].l, 1);
    }
    for (i = 1; i <= m; i++) printf("%lld\n", ans[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值