P8868 [NOIP2022] 比赛 题解

Description

小 N 和小 O 会在 2022 年 11 月参加一场盛大的程序设计大赛 NOIP!小 P 会作为裁判主持竞赛。小 N 和小 O 各自率领了一支 nn 个人的队伍,选手在每支队伍内都是从 11nn 编号。每一个选手都有相应的程序设计水平。具体的,小 N 率领的队伍中,编号为 ii1in1 \leq i \leq n)的选手的程序设计水平为 aia _ i;小 O 率领的队伍中,编号为 ii1in1 \leq i \leq n)的选手的程序设计水平为 bib _ i。特别地,{ai}\{a _ i\}{bi}\{b _ i\} 还分别构成了从 11nn 的排列。

每场比赛前,考虑到路途距离,选手连续参加比赛等因素,小 P 会选择两个参数 l,rl, r1lrn1 \leq l \leq r \leq n),表示这一场比赛会邀请两队中编号属于 [l,r][l, r] 的所有选手来到现场准备比赛。在比赛现场,小 N 和小 O 会以掷骰子的方式挑选出参数 p,qp, qlpqrl \leq p \leq q \leq r),只有编号属于 [p,q][p, q] 的选手才能参赛。为了给观众以最精彩的比赛,两队都会派出编号在 [p,q][p, q] 内的、程序设计水平值最大的选手参加比赛。假定小 N 派出的选手水平为 mam _ a,小 O 派出的选手水平为 mbm _ b,则比赛的精彩程度为 ma×mbm _ a \times m _ b

NOIP 总共有 QQ 场比赛,每场比赛的参数 l,rl, r 都已经确定,但是 p,qp, q 还没有抽取。小 P 想知道,对于每一场比赛,在其所有可能的 p,qp, qlpqrl \leq p \leq q \leq r)参数下的比赛的精彩程度之和。由于答案可能非常之大,你只需要对每一场答案输出结果对 2642 ^ {64} 取模的结果即可。

link

Solution

考虑把询问离线下来然后对 rr 做扫描线。

xi=maxj=iraj,yi=maxj=irbjx_i=\max\limits_{j=i}^{r}{a_j},y_i=\max\limits_{j=i}^{r}{b_j}

那么区间 [l0,r0][l_0,r_0] 的答案就是 rr[l0,r0][l_0,r_0]xl0×yl0x_{l_0}\times y_{l_0} 的历史版本和 SS

首先可以用单调栈维护对于每个 rr,他能控制 xx 的范围和控制 yy 的范围,所以当 rr 往右移一格,只会有三种操作:

  1. 将区间的 xx 更改为 ara_r
  2. 将区间的 yy 更改为 ara_r
  3. 对于 [1,r][1,r] 里的所有 ii,让 SiS_ixi×yix_i\times y_i

最后需要多次查询区间 SS 之和。


考虑用线段树维护,容易发现每次操作之后 SS 的增量一定可以表示为:addx×X+addy×Y+addxy×XY+addcadd_{x}\times X+add_y\times Y+add_{xy}\times XY+add_c,其中 addx,addy,addxy,addcadd_x,add_y,add_{xy},add_c 都是常数。

所以可以维护懒标记 tag=(setx,sety,addx,addy,addxy,addc)tag=(set_x,set_y,add_x,add_y,add_{xy},add_c) 分别表示区间 xx 赋值标记,区间 yy 赋值标记和四个系数。

考虑怎样合并两个懒标记 tag1,tag2tag_1,tag_2

这里需要分类讨论 tag1tag_1 是否有 xx 赋值和 yy 赋值。

  1. 如果 xxyy 都有赋值,那么只用更新 addcadd_c

  2. 如果 xx 有,yy 没有,那么可以用 tag2tag_2addxyadd_{xy}addyadd_y 更新 addtadd_taddxadd_x 更新 addcadd_cxx 没有 yy 没有是同理的)。

  3. 如果 xxyy 都没有,那么只要将 tag2tag_2addxadd_x 更新 addxadd_xaddyadd_y 更新 addyadd_yaddxyadd_{xy} 更新 addxyadd_xy 即可。

不要忘记 addcadd_c 要直接合并。

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

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#include <bits/stdc++.h>

// #define int int64_t

using u64 = uint64_t;

const int kMaxN = 2.5e5 + 5;

struct Node {
u64 sumx, sumy, sumxy, sum;

Node() {}
Node(u64 _sumx, u64 _sumy, u64 _sumxy, u64 _sum) : sumx(_sumx), sumy(_sumy), sumxy(_sumxy), sum(_sum) {}

friend Node operator +(Node n1, Node n2) {
return {n1.sumx + n2.sumx, n1.sumy + n2.sumy, n1.sumxy + n2.sumxy, n1.sum + n2.sum};
}
} t[kMaxN * 4];

struct Tag {
u64 setx, sety, addx, addy, addxy, addc; // x 的赋值,y 的赋值,x 对 sum 的系数,y 对 sum 的系数,xy 对 sum 的系数,常数对 sum 的系数

Tag() {}
Tag(u64 _setx, u64 _sety, u64 _addx, u64 _addy, u64 _addxy, u64 _addc) : setx(_setx), sety(_sety), addx(_addx), addy(_addy), addxy(_addxy), addc(_addc) {}

friend Tag operator +(Tag t1, Tag t2) { // t1:前面的,t2:后面的
Tag ret = t1;
if (t2.setx) ret.setx = t2.setx;
if (t2.sety) ret.sety = t2.sety;
if (t1.setx) {
ret.addc += t1.setx * t2.addx;
if (t1.sety) ret.addc += t1.sety * t2.addy + t1.setx * t1.sety * t2.addxy;
else ret.addy += t2.addy + t1.setx * t2.addxy;
} else {
ret.addx += t2.addx;
if (t1.sety) ret.addx += t1.sety * t2.addxy, ret.addc += t1.sety * t2.addy;
else ret.addy += t2.addy, ret.addxy += t2.addxy;
}
ret.addc += t2.addc;
return ret;
}
} tag[kMaxN * 4];

int n, q;
int a[kMaxN], b[kMaxN], la[kMaxN], lb[kMaxN], stka[kMaxN], stkb[kMaxN];
u64 ans[kMaxN];
std::vector<std::pair<int, int>> vec[kMaxN];

Node merge(Node a, Tag t, int len) {
Node ret = a;
if (t.setx) ret.sumx = t.setx * len;
if (t.sety) ret.sumy = t.sety * len;

if (t.setx && t.sety) ret.sumxy = t.setx * t.sety * len;
else if (t.setx) ret.sumxy = t.setx * a.sumy;
else if (t.sety) ret.sumxy = t.sety * a.sumx;

ret.sum += a.sumx * t.addx + a.sumy * t.addy + a.sumxy * t.addxy + t.addc * len;
return ret;
}

struct SGT {
Node t[kMaxN * 4];
Tag tag[kMaxN * 4];

void pushup(int x) {
t[x] = t[x << 1] + t[x << 1 | 1];
}

void addtag(int x, int l, int r, Tag tg) {
t[x] = merge(t[x], tg, r - l + 1), tag[x] = tag[x] + tg;
}

void pushdown(int x, int l, int r) {
if (!tag[x].setx && !tag[x].sety && !tag[x].addx && !tag[x].addy && !tag[x].addxy && !tag[x].addc) return;
int mid = (l + r) >> 1;
addtag(x << 1, l, mid, tag[x]), addtag(x << 1 | 1, mid + 1, r, tag[x]);
tag[x] = {0, 0, 0, 0, 0, 0};
}

void update(int x, int l, int r, int ql, int qr, Tag t) {
if (l > qr || r < ql) {
return;
} else if (l >= ql && r <= qr) {
return addtag(x, l, r, t);
}
pushdown(x, l, r);
int mid = (l + r) >> 1;
update(x << 1, l, mid, ql, qr, t), update(x << 1 | 1, mid + 1, r, ql, qr, t);
pushup(x);
}

u64 query(int x, int l, int r, int ql, int qr) {
if (l > qr || r < ql) {
return 0;
} else if (l >= ql && r <= qr) {
return t[x].sum;
}
pushdown(x, l, r);
int mid = (l + r) >> 1;
return query(x << 1, l, mid, ql, qr) + query(x << 1 | 1, mid + 1, r, ql, qr);
}
} sgt;

void dickdreamer() {
int _case;
std::cin >> _case >> n;
for (int i = 1; i <= n; ++i) std::cin >> a[i];
for (int i = 1; i <= n; ++i) std::cin >> b[i];
std::cin >> q;
for (int i = 1; i <= q; ++i) {
int l, r;
std::cin >> l >> r;
vec[r].emplace_back(l, i);
}
int topa = 0, topb = 0;
for (int i = 1; i <= n; ++i) {
for (; topa && a[stka[topa]] < a[i]; --topa) {}
for (; topb && b[stkb[topb]] < b[i]; --topb) {}
la[i] = stka[topa], lb[i] = stkb[topb];
stka[++topa] = i, stkb[++topb] = i;
sgt.update(1, 1, n, la[i] + 1, i, {a[i], 0, 0, 0, 0, 0});
sgt.update(1, 1, n, lb[i] + 1, i, {0, b[i], 0, 0, 0, 0});
sgt.update(1, 1, n, 1, i, {0, 0, 0, 0, 1, 0});
for (auto p : vec[i])
ans[p.second] = sgt.query(1, 1, n, p.first, i);
}
for (int i = 1; i <= q; ++i)
std::cout << ans[i] << '\n';
}

int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}

P8868 [NOIP2022] 比赛 题解
https://sobaliuziao.github.io/2023/12/28/post/348b01f1.html
作者
Egg_laying_master
发布于
2023年12月28日
许可协议