题解:P9353 [JOI 2023 Final] 现代机器 / Modern Machine
感觉很好的题啊。
题意:感觉总结不是很总结地明白,直接看题面吧。
Bitaro 收到了一个 JOI 机器作为生日礼物。
JOI 机器由一个球、\(N\) 个灯光瓷砖和 \(M\) 个按钮组成。灯光瓷砖从 \(1\) 到 \(N\) 编号。当 Bitaro 打开电源时,灯光瓷砖 \(i\) (\(1 \leq i \leq N\)) 会发出颜色为 \(C_i\)(蓝色(\(\texttt B\))或红色(\(\texttt R\)))的光。按钮从 \(1\) 到 \(M\) 编号。
如果 Bitaro 按下按钮 \(j\) (\(1 \leq j \leq M\)),会发生以下情况。
- 球被放置在灯光瓷砖 \(A_j\) 上。
- 灯光瓷砖 \(A_j\) 变为红色(无论其原始颜色如何)。
- 在球被移除之前,执行以下操作。 设 \(p\) 为球当前所在的灯光瓷砖的索引。
- 如果灯光瓷砖 \(p\) 是蓝色的,灯光瓷砖 \(p\) 变为红色。之后,如果 \(p = 1\),球被移除。否则,球移动到灯光瓷砖 \(p - 1\)。
- 如果灯光瓷砖 \(p\) 是红色的,灯光瓷砖 \(p\) 变为蓝色。之后,如果 \(p = N\),球被移除。否则,球移动到灯光瓷砖 \(p + 1\)。
Bitaro 对 JOI 机器很感兴趣。他计划进行 \(Q\) 次实验。在第 \(k\) 次实验中(\(1 \leq k \leq Q\)),在 Bitaro 打开电源后,Bitaro 按顺序按下按钮 \(L_k, L_{k} + 1, \dots , R_k\)。在 Bitaro 按下一个按钮后,他不会按下下一个按钮,并等待球被移除。
给定 JOI 机器的信息和实验,编写一个程序来计算每次实验结束时颜色为红色的灯光瓷砖的数量。
做法:
首先我们先来看看这个操作会对序列产生什么影响,发现他是往右找到第一个蓝色再找到左边第一个红色轮流轮流这样的,模拟几下,发现操作等于前 \(i\) 个蓝染红和后 \(n-i+1\) 个红染蓝,并且因为整个序列中不能同时存在 \(i\) 个蓝和 \(n-i+1\) 个红,所以可以从整个序列的情况直接快速计算我现在是哪种情况。
直接模拟容易做到 \(O(nmq)\)。然后啥也不会了,让我们看看部分分。
部分分里有一档是前缀是红色,后缀是蓝色,我们发现这种情况下这个结构不会变化,仍然会保证前缀红后缀蓝的情况。我们分讨一下情况,假设前 \(k\) 个是红色,修改位置为 \(p\)。
-
\(p\le k\):如果 \(n-k \ge p\),那么 \(k\to k+p\);否则 \(k\to k-(n-p+1)\)。
-
\(p>k\):如果 \(n-k-1\ge p\),那么 \(k\to k+p+1\),因为还多把 \(p\) 这个位置多变成红色;否则 \(k\to k-(n-p)\)。
我们发现一个很好的事情,考虑对这个东西模 \(n+1\),那么就变成:
-
\(k < p\),那么 \(k\to k+p+1\)。
-
\(k\ge p\),那么 \(k\to k+p\)。
这个东西可以直接维护分段函数,因为他等于一个线段平移,维护每个区间的结尾和加了多少可以方便维护。还有一个做法是我们直接把区间离线下来,然后 fhq 维护平移也可以,我写的是分段函数的写法。
但是这是部分分做法,我们多数时候当然不满足这个条件。我们考虑维护目前最左端连续的红色,右端的蓝色长度分别为 \(x,y\)。我们发现,如果修改位置为 \(x<p\le n-y\),那么好处是 \(x,y\) 中有一个翻倍,这很好,操作次数是 \(\log\) 的。但是比如,\(x=y=1\),我一直操作 \(p=1\),那么次数就变成 \(n\)。但是有一个好处,如果落在 \([1,x],[n-y+1,n]\) 的范围内,如果落在前者,那么一定是把 \(x\) 增大,否则一定把 \(y\) 增大,这个事情让我们可以直接去找到 \([x+1,n-y]\) 中下一个操作的位置,其他的我们可以 \(O(1)\) 计算,你可以采用主席树的方式来找,就是按操作序列从后往前扫,维护每个位置上一次出现位置即可。但是还有一个做法,我们可以考虑倍增分块,这样我们就只用维护 \(\log^2n\) 种区间情况下一次操作的位置,并且这样同样次数还是 \(\log n\) 的,我写的是倍增分块的做法。
然后大概就做完了,有一些蓝红色的分讨注意一下就可以了。
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 5, V = 21;
int n, m, id[maxn], a[maxn], q;
struct Seg {
int ed, k;
friend bool operator<(Seg x, Seg y) {
return x.ed < y.ed;
}
};
struct Segtree {
vector<Seg> tr[maxn << 2];
void build(int l, int r, int t) {
if(l == r) {
if(a[l] == n)
tr[t].push_back(Seg{n - 1, 0}),
tr[t].push_back(Seg{n, -1});
else if(a[l] * 2 > n)
tr[t].push_back(Seg{n - a[l] - 1, a[l] + 1}),
tr[t].push_back(Seg{a[l] - 1, a[l] + 1 - (n + 1)}),
tr[t].push_back(Seg{n, a[l] - (n + 1)});
else
tr[t].push_back(Seg{a[l] - 1, a[l] + 1}),
tr[t].push_back(Seg{n - a[l], a[l]}),
tr[t].push_back(Seg{n, a[l] - (n + 1)});
return ;
}
int mid = l + r >> 1;
build(l, mid, t << 1), build(mid + 1, r, t << 1 | 1);
int L = 0, R, v;
for (int i = 0; i < tr[t << 1].size(); i++) {
R = tr[t << 1][i].ed, v = tr[t << 1][i].k;
int bg = lower_bound(tr[t << 1 | 1].begin(), tr[t << 1 | 1].end(), Seg{L + v, 0}) - tr[t << 1 | 1].begin();
int ed = lower_bound(tr[t << 1 | 1].begin(), tr[t << 1 | 1].end(), Seg{R + v, 0}) - tr[t << 1 | 1].begin();
for (bg; bg < ed; bg++)
tr[t].push_back(Seg{tr[t << 1 | 1][bg].ed - v, v + tr[t << 1 | 1][bg].k});
tr[t].push_back(Seg{R, v + tr[t << 1 | 1][ed].k}), L = R + 1;
}
// cout << l << " " << r << " " << tr[t][tr[t].size() - 1].ed << " " << L << " " << R << endl;
}
void query(int l, int r, int x, int y, int t, int &p) {
if(x <= l && r <= y) {
int pos = lower_bound(tr[t].begin(), tr[t].end(), Seg{p, 0}) - tr[t].begin();
// cout << pos << " " << tr[t].size() << endl;
p += tr[t][pos].k;
return ;
}
int mid = l + r >> 1;
if(x <= mid)
query(l, mid, x, y, t << 1, p);
if(mid < y)
query(mid + 1, r, x, y, t << 1 | 1, p);
}
} tree;
int lx[maxn], lp[maxn], rx[maxn], rp[maxn], lt, rt, lg[maxn], s[maxn];
int nxt[V][V][maxn], sl[V][V][maxn], sr[V][V][maxn];
void prepare() {
for (int i = 1; i <= n + 1; i++) {
if(id[i] != 1)
lx[++lt] = i - 1,
lp[i - 1] = lt;
}
for (int i = n; i >= 0; i--)
if(id[i] != 0) {
rx[++rt] = n - i,
rp[i] = rt;
//cout << rt << " " << i << " " << rx[1] << endl;
}
// cout << rx[1] << "asdlkfjsadlkfjsadkfjfdg " << lx[1] << endl;
lg[0] = 0;
for (int i = 1; i <= n; i++)
lg[i] = lg[i >> 1] + 1, s[i] = s[i - 1] + (!id[i]);
for (int tx = 0; tx <= V - 1; tx++)
for (int ty = 0; ty <= V - 1; ty++) {
int x = (tx == 0 ? 0 : (1 << tx - 1)), y = (ty == 0 ? 0 : (1 << ty - 1));
nxt[tx][ty][m + 1] = m + 1;
for (int i = m; i >= 1; i--) {
if(a[i] > x && a[i] <= n - y)
nxt[tx][ty][i] = i;
else
nxt[tx][ty][i] = nxt[tx][ty][i + 1];
}
for (int i = 1; i <= m; i++)
sl[tx][ty][i] = sl[tx][ty][i - 1] + (a[i] <= x ? a[i] : 0),
sr[tx][ty][i] = sr[tx][ty][i - 1] + (a[i] > n - y ? n - a[i] : 0);
}
// cout << nxt[0][0][1] << " " << lx[1] << " " << rx[1] << endl;
}
int main() {
// freopen("test.in", "r", stdin);
// freopen("std.out", "w", stdout);
cin >> n >> m;
for (int i = 1; i <= n; i++) {
char c; cin >> c;
id[i] = c == 'R';
// cout << "adf" << endl;
}
id[0] = -1; id[n + 1] = -1;
for (int i = 1; i <= m; i++)
cin >> a[i];
//cout << "adf" << endl;
tree.build(1, m, 1);
prepare();
cin >> q;
while(q--) {
int l, r; cin >> l >> r;
int L = l - 1, R, x = 1, y = 1, qry = -1;
while(1) {
int X = lg[lx[x]], Y = lg[rx[y]];
R = min(r + 1, nxt[X][Y][L + 1]); R--;
// cout << L << " " << R << " " << " " << X << " " << Y << " " << lx[x] << " " << rx[y] << " " << x << " " << y << endl;
int sx = x + sl[X][Y][R] - sl[X][Y][L], sy = y + sr[X][Y][R] - sr[X][Y][L];
// cout << lx[min(sx, lt)] << " " << rx[min(sy, rt)] << " " << sx << " " << sy << " " << rx[sy] << endl;
// cout << L << " " << R << " " << sx << " " << sy << " " << lx[sx] << " " << rx[sy] << endl;
if(lx[min(sx, lt)] + rx[min(sy, rt)] < n) {
x = sx, y = sy; L = R + 1;
// cout << L << " laskdgjsag" << R << " " << x << " " << y << endl;
if(L == r + 1) {
// cout << "asdf" << endl;
break;
}
int sum = s[n - rx[y]] - s[lx[x]] + rx[y];
int pos = a[L] + (a[L] >= n - rx[y] + 1 ? 1 : (a[L] <= lx[x] ? 0 : !id[a[L]]));
// cout << sum << " " << pos << endl;
if(pos <= sum) {
if(pos >= sum - rx[y]) {
L = L + 1;
qry = n - rx[y] + (pos - (sum - rx[y]));
break;
}
else
x += pos;
}
else {
sum = n - sum, pos = (n - a[L] + (a[L] >= n - rx[y] + 1 ? 0 : (a[L] <= lx[x] ? 1 : id[a[L]])));
// cout << pos << endl;
if(pos >= sum - lx[x]) {
L = L + 1;
qry = lx[x] - (pos - (sum - lx[x]));
break;
}
else
y += pos;
}
}
else {
int lpos = L, rpos = R;
while(lpos + 1 < rpos) {
int mid = lpos + rpos >> 1;
sx = x + sl[X][Y][mid] - sl[X][Y][L], sy = y + sr[X][Y][mid] - sr[X][Y][L];
if(lx[min(sx, lt)] + rx[min(sy, rt)] < n)
lpos = mid;
else
rpos = mid;
}
R = lpos;
sx = x + sl[X][Y][R] - sl[X][Y][L], sy = y + sr[X][Y][R] - sr[X][Y][L];
x = sx, y = sy; L = R + 1;
if(L == r + 1)
break;
int sum = s[n - rx[y]] - s[lx[x]] + rx[y];
int pos = a[L] + (a[L] >= n - rx[y] + 1 ? 1 : (a[L] <= lx[x] ? 0 : !id[a[L]]));
if(pos <= sum) {
if(pos >= sum - rx[y]) {
L = L + 1;
qry = n - rx[y] + (pos - (sum - rx[y]));
break;
}
else
x += pos;
}
else {
sum = n - sum, pos = (n - a[L] + (a[L] >= n - rx[y] + 1 ? 0 : (a[L] <= lx[x] ? 1 : id[a[L]])));
// cout << pos << " " << sum << " " << y << " " << x << endl;
if(pos >= sum - lx[x]) {
L = L + 1;
qry = lx[x] - (pos - (sum - lx[x]));
break;
}
else
y += pos;
}
break;
}
}
// cout << x << " " << y << " " << lx[x] << " " << rx[y] << " " << L << " " << r << endl;
if(qry == -1) {
// cout << s[n - rx[y]] << "adf " << s[lx[x]] << " " << lx[x] << " " << n - rx[y] << endl;
int sum = s[n - rx[y]] - s[lx[x]] + rx[y];
cout << n - sum << endl;
}
else {
// cout << L << " " << r << " " << qry << endl;
if(L <= r)
tree.query(1, m, L, r, 1, qry);
cout << qry << endl;
}
}
return 0;
}
/*
10 10
RRRRRRRRRR
4 7 5 10 6 7 5 2 1 3
1
1 9
*/

浙公网安备 33010602011771号