Loading

【题解】P8868 [NOIP2022] 比赛

梦碎 NOIP2022.

思路来自 rqy 姐姐,好工作。

见过的题目中比较玄学的数据结构,理性分析似乎要用双半群……?

思路

扫描线 + 线段树历史版本和。

理论上 52pts 应该有一个单次询问 \(O(n \log n)\) 的分治做法,但是没有细想。

首先发现是求所有子区间的权值和,感觉上不太能转化成和端点有关的问题,所以只能考虑扫描线做。

考虑把所有询问离线下来,然后从左到右扫描线。在扫描线的同时,维护每个位置的答案。

形式化地,令 \(X_{l, r} = \max\limits_{i = l}^r a_i, Y_{l, r} = \max\limits_{i = l}^r b_i\).

对于当前的右端点,考虑到它的贡献是把 \(X_{k, r}\)\(Y_{k, r}\) 的一段后缀分别更改成 \(a_r, b_r\).

于是考虑用一棵线段树维护区间覆盖,然后答案就是维护 \(XY\) 的线段树历史版本和。

形式化的描述是考虑在扫描线的同时维护:\(S_{l, r} = \sum\limits_{r^{\prime} = l}^r X_{l, r^{\prime}} Y_{l, r^{\prime}}\).

询问 \([l, r]\) 的答案就是 \(\sum\limits_{i = l}^r S_{i, r}\).

类似朴素的线段树历史版本和,考虑用六个标记维护当前区间和的变化量。下传标记的时候钦定一下标记之间的优先级,然后转化一下。

时间复杂度 \(O(q \log n)\)

代码

#include <cstdio>
#include <vector>
using namespace std;

#define ls (k << 1)
#define rs (k << 1 | 1)
#define il inline

typedef unsigned long long ull;

const int maxn = 2.5e5 + 5;
const int maxq = 2.5e5 + 5;
const int sgt_sz = (maxn << 2);

struct node
{
    ull s, x, y, xy;
} tr[sgt_sz];

struct tag
{
    ull cx, cy;
    ull add_x, add_y, add_xy, add_c;

    bool check() { return (cx || cy || add_x || add_y || add_xy || add_c); }
} lazy[sgt_sz];

struct Query
{
    int l, id;
} ;
vector<Query> qry[maxn];

int t, n, q;
int top, stk[maxn];
int a[maxn], b[maxn];
int la[maxn], lb[maxn];
ull ans[maxq];

il int read()
{
    int res = 0;
    char ch = getchar();
    while ((ch < '0') || (ch > '9')) ch = getchar();
    while ((ch >= '0') && (ch <= '9')) res = res * 10 + ch - '0', ch = getchar();
    return res;
}

il void write(ull x)
{
    if (x > 9) write(x / 10);
    putchar(x % 10 + '0');
}

void push_up(int k) { tr[k] = (node){tr[ls].s + tr[rs].s, tr[ls].x + tr[rs].x, tr[ls].y + tr[rs].y, tr[ls].xy + tr[rs].xy}; }

void upd_tag(int k, int len, tag t)
{
    // update lazy
    auto &[cx, cy, add_x, add_y, add_xy, add_c] = lazy[k];
    if (cx && cy) add_c += t.add_xy * cx * cy + t.add_x * cx + t.add_y * cy + t.add_c;
    else if (cx) add_c += t.add_x * cx + t.add_c, add_y += t.add_xy * cx + t.add_y;
    else if (cy) add_c += t.add_y * cy + t.add_c, add_x += t.add_xy * cy + t.add_x;
    else add_x += t.add_x, add_y += t.add_y, add_xy += t.add_xy, add_c += t.add_c;
    if (t.cx) cx = t.cx;
    if (t.cy) cy = t.cy;
    // update info
    auto &[s, x, y, xy] = tr[k];
    s += t.add_x * x + t.add_y * y + t.add_xy * xy + t.add_c * len;
    if (t.cx && t.cy) xy = t.cx * t.cy * len, x = t.cx * len, y = t.cy * len;
    else if (t.cx) xy = t.cx * y, x = t.cx * len;
    else if (t.cy) xy = x * t.cy, y = t.cy * len;
}

void push_down(int k, int l, int r)
{
    if (lazy[k].check())
    {
        int mid = (l + r) >> 1;
        upd_tag(ls, mid - l + 1, lazy[k]);
        upd_tag(rs, r - mid, lazy[k]);
        lazy[k] = (tag){0, 0, 0, 0, 0, 0};
    }
}

void update(int k, int l, int r, int ql, int qr, tag w)
{
    if ((l >= ql) && (r <= qr)) return upd_tag(k, r - l + 1, w), void();
    push_down(k, l, r);
    int mid = (l + r) >> 1;
    if (ql <= mid) update(ls, l, mid, ql, qr, w);
    if (qr > mid) update(rs, mid + 1, r, ql, qr, w);
    push_up(k);
}

ull query(int k, int l, int r, int ql, int qr)
{
    if ((l >= ql) && (r <= qr)) return tr[k].s;
    push_down(k, l, r);
    int mid = (l + r) >> 1; ull res = 0;
    if (ql <= mid) res += query(ls, l, mid, ql, qr);
    if (qr > mid) res += query(rs, mid + 1, r, ql, qr);
    return res;
}

void work(int *a, int *lst)
{
    top = 0;
    for (int i = 1; i <= n; i++)
    {
        while (top && (a[i] >= a[stk[top]])) top--;
        lst[i] = (top ? stk[top] + 1 : 1);
        stk[++top] = i;
    }
}

int main()
{
    // freopen("match2.in", "r", stdin);
    // freopen("match2.out", "w", stdout);
    t = read(), n = read();
    for (int i = 1; i <= n; i++) a[i] = read();
    for (int i = 1; i <= n; i++) b[i] = read();
    q = read();
    for (int i = 1, l, r; i <= q; i++)
    {
        l = read(), r = read();
        Query cur = (Query){l, i};
        qry[r].push_back(cur);
    }
    work(a, la), work(b, lb);
    for (int i = 1; i <= n; i++)
    {
        update(1, 1, n, la[i], i, (tag){a[i], 0, 0, 0, 0, 0});
        update(1, 1, n, lb[i], i, (tag){0, b[i], 0, 0, 0, 0});
        update(1, 1, n, 1, i, (tag){0, 0, 0, 0, 1, 0});
        for (auto &[l, id] : qry[i]) ans[id] = query(1, 1, n, l, i);
    }
    for (int i = 1; i <= q; i++) write(ans[i]), putchar('\n');
    return 0;
}
posted @ 2023-03-09 15:26  kymru  阅读(104)  评论(0编辑  收藏  举报