题解:CF2115F1 Gellyfish and Lycoris Radiata (Easy Version)
节选自:Codeforces Round 1028 (Div. 1)
这题非常好玩,也非常达芬,我做了 \(1\) 天半才过。
我们考虑到问题是在线,因此可以想到支持在线的操作分块(虽然我没想到),每 \(\sqrt q\) 个操作分成一个操作块。考虑到一次操作最多会使序列新增一个颜色段(颜色段内的所有集合都是一样的),因此一个操作块最多会使一个序列分成 \(\mathcal O(\sqrt q)\) 个颜色段,对于每个颜色段 \(i = [l_i, r_i]\),我们维护一个队列 \(q_i\) 表示段内的集合,\(rev_i\) 表示该段是否翻转,\([rl_i, rr_i]\) 表示这个颜色段对应原序列中的哪一段。
此时 \(1\) 操作和 \(2\) 操作比较好维护。我们找到所有需要插入或翻转的颜色段,如果 \(r\) 正好在一个颜色段 \(i\) 中间,我们就需要将该颜色段分裂。注意此时 \([rl_i, rr_i]\) 的分裂和 \(rev_i\) 有关。假设我们要将颜色段 \(i = [6, 10]\) 在 \(7\) 处分裂,\(i\) 对应的原序列中的区间为 \([1, 5]\)。如果 \(rev_i = 0\),那么直接将 \([1, 5]\) 分裂成 \([1, 2]\) 和 \([3, 5]\) 即可;否则就应该分裂成 \([4, 5]\) 和 \([1, 3]\)。队列 \(q_i\) 就直接复制,时间复杂度将在后文分析。
操作 \(3\) 不好维护,但是我们可以延迟删除。如果 \(x\) 小于等于当前操作数,就将 \(x\) 标记成删除。不过此时队列还需要维护一个 \(be_i\),表示最小的没被删除的数字。由于每次被插入集合的都是当前操作编号,而编号是单增的,因此 \(be_i\) 也是最靠前的没被删除的数字。
现在来处理查询操作。对于一个位置 \(p\),我们可以二分查找出当前操作块中包含 \(p\) 的颜色段 \(i\),然后遍历 \(i\) 的队列 \(q_i\),如果枚举到的数字没被删除,那么根据刚才的推导,这一定是这个队列中最小的元素,用它更新答案即可;否则就将 \(be_i\) 加 \(1\)。由于我们记录了 \([rl_i, rr_i]\),因此我们知道 \(p\) 在经过当前操作块之前所处的位置 \(p'\),也就是 \(p\) 在上一个操作块中所处的位置,因此我们不断往下查找即可。
现在来分析时间复杂度。先来考虑修改操作,我们发现一个操作块中最多会被插入 \(\mathcal O(\sqrt q)\) 个数字,而最多只会分裂 \(\mathcal O(\sqrt q)\) 次,每次都会将这 \(\mathcal O(\sqrt q)\) 个数字复制一遍,因此一个操作块内操作的时间复杂度是 \(\mathcal O(q)\),这部分的总时间复杂度就是 \(\mathcal O(q \sqrt q)\)。而查询时,由于我们记录了 \(be_i\),而 \(be_i\) 是单调不降的,因此每个颜色段只会被扫一遍,而颜色段的数量长度和是 \(\mathcal O(n \sqrt q)\) 的。加上二分查找的时间复杂度,这一部分的时间复杂度就是 \(\mathcal O(n \sqrt q \log \sqrt q)\),总时间复杂度就是 \(\mathcal O(q \sqrt q + n \sqrt q \log \sqrt q)\)。
完整代码:
点击查看代码
#include <bits/stdc++.h>
using namespace std;
inline int read(){
int x = 0; bool f(0); char ch = getchar();
while(!isdigit(ch)) f = (ch == '-'), ch = getchar();
while(isdigit(ch)) x = (x<<3) + (x<<1) + (ch^48), ch = getchar();
return f ? -x : x;
}
inline void write(int x){
if(x < 0) x = -x, putchar('-');
static short Stack[64], top(0);
do Stack[++ top] = x % 10, x /= 10; while(x);
while(top) putchar(Stack[top--] | 48);
}
const int N = 2e5 + 9, B = 319, INF = 1e9 + 9;
struct Segment{
int l, r, rl, rr;
inline bool operator < (const Segment &a) const{
return l < a.l;
}
} t[N];
struct Node{
int pre, nex;
} e[N];
int head[N];
inline void add(int id, int x){//在 id 后面插入 x
e[x].nex = e[id].nex;
e[x].pre = id;
e[id].nex = x;
}
inline void del(int id){
e[e[id].nex].pre = e[id].pre;
e[e[id].pre].nex = e[id].nex;
}
int era[N], rev[N], q[N][B], be[N], ed[N], n, qq, tot = 1, cnt, num = 1, lastans;
void split(int i, int b){
tot++;
for(int j = be[i]; j <= ed[i]; j++)
q[tot][++ed[tot]] = q[i][j];
rev[tot] = rev[i];
int tmp1, tmp2, tmp3, tmp4;
if(rev[i]){
tmp1 = t[i].rr - (b - t[i].l + 1) + 1;
tmp2 = t[i].rr;
tmp3 = t[i].rl;
tmp4 = t[i].rr - (b - t[i].l + 1);
} else {
tmp1 = t[i].rl;
tmp2 = t[i].rl + (b - t[i].l + 1) - 1;
tmp3 = t[i].rl + (b - t[i].l + 1);
tmp4 = t[i].rr;
}
t[tot] = Segment{b + 1, t[i].r, tmp3, tmp4};
t[i].r = b;
t[i].rl = tmp1;
t[i].rr = tmp2;
add(i, tot);
}
int st[N][B], sum[B];
vector <int> vec;
int main(){
for(int i = 1; i < N; i++)
be[i] = 1;
n = read();
qq = read();
int k = sqrt(qq), qqq = qq;
t[tot]= Segment{1, n, 1, n};
e[tot].pre = 0;
e[0].pre = -1;
e[0].nex = tot;
e[tot].nex = 2 * n;
e[2 * n].pre = tot;
e[2 * n].nex = -1;
head[1] = tot;
while(qqq--){
cnt++;
int a, b, c;
a = read();
b = read();
c = read();
if(a == 1 || a == 2)
b = (b + lastans - 1) % n + 1;
else
b = (b + lastans - 1) % qq + 1;
c = (c + lastans - 1) % n + 1;
if(a == 1){
int he = 0;
for(int i = head[num]; i != 2 * n; i = e[i].nex){
he += t[i].r - t[i].l + 1;
if(he == b){
q[i][++ed[i]] = cnt;
break;
}
if(he > b){
split(i, b);
q[i][++ed[i]] = cnt;
break;
}
q[i][++ed[i]] = cnt;
}
} else if(a == 2){
int he = 0;
vec.clear();
for(int i = head[num]; i != 2 * n; i = e[i].nex){
he += t[i].r - t[i].l + 1;
if(he == b){
vec.push_back(i);
del(i);
break;
}
if(he > b){
split(i, b);
vec.push_back(i);
del(i);
break;
}
vec.push_back(i);
del(i);
}
for(int i = 0; i <= (int)vec.size() - 1; i++){
int L = t[vec[i]].l, R = t[vec[i]].r;
t[vec[i]].l = b - R + 1;
t[vec[i]].r = b - L + 1;
add(0, vec[i]);
rev[vec[i]] ^= 1;
}
head[num] = vec[vec.size() - 1];
} else if(a == 3){
if(b <= cnt)
era[b] = true;
}
sum[num] = 0;
for(int i = head[num]; i != 2 * n; i = e[i].nex)
st[num][++sum[num]] = i;
int ans = INF;
for(int i = num; i >= 1; i--){
int L = 1, R = sum[i], res = 0, j;
while(L <= R){
int mid = (L + R) >> 1;
if(t[st[i][mid]].l <= c)
L = mid + 1, res = mid;
else
R = mid - 1;
}
j = st[i][res];
for(int l = be[j]; l <= ed[j]; l++){
if(!era[q[j][l]]){
ans = min(ans, q[j][l]);
break;
} else
be[j]++;
}
c = t[j].rl + (c - t[j].l + 1) - 1;
if(rev[j])
c = t[j].rl + t[j].rr - c;
}
if(ans == INF)
ans = 0;
write(lastans = ans);
putchar('\n');
if(cnt % k == 0){
sum[num] = 0;
for(int i = head[num]; i != 2 * n; i = e[i].nex)
st[num][++sum[num]] = i;
num++, tot++;
t[tot] = Segment{1, n, 1, n};
e[tot].pre = 0;
e[0].pre = -1;
e[0].nex = tot;
e[tot].nex = 2 * n;
e[2 * n].pre = tot;
e[2 * n].nex = -1;
head[num] = tot;
}
}
return 0;
}
本文来自博客园,作者:Orange_new,转载请注明原文链接:https://www.cnblogs.com/JPGOJCZX/p/19161415

浙公网安备 33010602011771号