【题解】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;
}