题解:AT_stpc2025_1_m Many Approaches
违规用户名576639 · · 题解
考虑只有 1 操作,怎么做。
如果允许离线的话,主席树是一个比较简单的做法。
但如果是在线的话,就要写可持久化主席树了,而且这种做法和 2 操作的关系不大,所以不考虑。
显然这个问题是有结合律的,考虑序列分块,记
考虑由于是所有不在
考虑线段树维护这个问题,那么预处理
对于散块直接模拟。
考虑 2 操作如何做。
本质是给定操作过后的区间,求操作前的区间。
简化问题,如果只有一个块怎么做,由于相对位置关系不变,所以通过块内的指令到达点
对于散块而言,已知经过一条指令后的点构成的区间
那么你就做完了,但是直接交上去会卡常,做一些小优化。由于相对顺序不变,所以并不需要直接去记录和
时间复杂度:
空间复杂度:
虽然此处线段树清空的复杂度是线性的,但是由于常数过大,所以需要将块长调得稍微大一点,我开到了 1500。此题在 qoj 上的编号为 16238。
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
const int B = 1500;
const int K = (N + B - 1) / B + 2;
struct Node{
int minx, maxx;
void Clean(){
minx = N;
maxx = 0;
}
Node operator + (const Node &b){
return {min(minx, b.minx), max(maxx, b.maxx)};
}
void operator = (const Node &b){
minx = b.minx, maxx = b.maxx;
}
}tr[4 * N];
int n, m, q, tot, a[N], sl[K], sr[K], team[N], s[K][N], sum[K][N], lazy[4 * N];
void build(int id, int l, int r){
lazy[id] = 0;
if(l == r){
tr[id] = {l, l};
return ;
}
int mid = (l + r) >> 1;
build((id << 1), l, mid);
build(((id << 1) | 1), mid + 1, r);
tr[id] = tr[id << 1] + tr[(id << 1) | 1];
}
void tag(int id){
tr[id << 1].minx += lazy[id];
tr[id << 1].maxx += lazy[id];
tr[(id << 1) | 1].minx += lazy[id];
tr[(id << 1) | 1].maxx += lazy[id];
lazy[id << 1] += lazy[id];
lazy[(id << 1) | 1] += lazy[id];
lazy[id] = 0;
}
//小于 x 的最后一个位置
int query(int id, int l, int r, int x){
if(l == r) return (tr[id].minx < x ? l : -1);
int mid = (l + r) >> 1;
tag(id);
if(tr[(id << 1) | 1].minx < x) return query(((id << 1) | 1), mid + 1, r, x);
return query((id << 1), l, mid, x);
}
//大于 x 的第一个位置
int query2(int id, int l, int r, int x){
if(l == r) return (tr[id].minx > x ? l : n + 1);
int mid = (l + r) >> 1;
tag(id);
if(tr[id << 1].maxx > x) return query2((id << 1), l, mid, x);
return query2(((id << 1) | 1), mid + 1, r, x);
}
void change(int id, int l, int r, int p, int q, int num){
if(p <= l && q >= r){
lazy[id] += num;
tr[id].minx += num;
tr[id].maxx += num;
return ;
}
if(r < p || l > q) return ;
int mid = (l + r) >> 1;
tag(id);
change((id << 1), l, mid, p, q, num);
change(((id << 1) | 1), mid + 1, r, p, q, num);
tr[id] = tr[id << 1] + tr[(id << 1) | 1];
}
void record(int id, int l, int r, int pos){
if(l == r){
s[pos][l] = tr[id].minx;
return ;
}
int mid = (l + r) >> 1;
tag(id);
record((id << 1), l, mid, pos);
record(((id << 1) | 1), mid + 1, r, pos);
}
int Query(int l, int r, int p){
if(team[l] == team[r]){
for(int i = l; i <= r; i++){
if(p < a[i]) p++;
else if(p > a[i]) p--;
}
return p;
}
for(int i = l; i <= sr[team[l]]; i++){
if(p < a[i]) p++;
else if(p > a[i]) p--;
}
for(int i = team[l] + 1; i < team[r]; i++){
p = s[i][p];
}
for(int i = sl[team[r]]; i <= r; i++){
if(p < a[i]) p++;
else if(p > a[i]) p--;
}
return p;
}
int Query2(int l, int r, int p){
if(team[l] == team[r]){
pair<int, int> seg = {p, p};
for(int i = r; i >= l; i--){
if(seg.second < a[i]){
if(seg.second == 1) return 0;
seg.second--;
seg.first = max(1, seg.first - 1);
}
else if(seg.first > a[i]){
if(seg.first + 1 > n) return 0;
seg.first++;
seg.second = min(n, seg.second + 1);
}
else{
seg.first = max(1, seg.first - 1);
seg.second = min(n, seg.second + 1);
if(seg.first > seg.second) return 0;
}
}
return seg.second - seg.first + 1;
}
pair<int, int> seg = {p, p};
for(int i = r; i >= sl[team[r]]; i--){
if(seg.second < a[i]){
if(seg.second == 1) return 0;
seg.second--;
seg.first = max(1, seg.first - 1);
}
else if(seg.first > a[i]){
if(seg.first + 1 > n) return 0;
seg.first++;
seg.second = min(n, seg.second + 1);
}
else{
seg.first = max(1, seg.first - 1);
seg.second = min(n, seg.second + 1);
if(seg.first > seg.second) return 0;
}
}
for(int i = team[r] - 1; i > team[l]; i--){
if(sum[i][seg.first - 1] + 1 > sum[i][seg.second]) return 0;
seg.first = sum[i][seg.first - 1] + 1;
seg.second = sum[i][seg.second];
}
for(int i = sr[team[l]]; i >= l; i--){
if(seg.second < a[i]){
if(seg.second == 1) return 0;
seg.second--;
seg.first = max(1, seg.first - 1);
}
else if(seg.first > a[i]){
if(seg.first + 1 > n) return 0;
seg.first++;
seg.second = min(n, seg.second + 1);
}
else{
seg.first = max(1, seg.first - 1);
seg.second = min(n, seg.second + 1);
if(seg.first > seg.second) return 0;
}
}
return seg.second - seg.first + 1;
}
int main(){
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> m >> q;
tot = (m + B - 1) / B;
for(int i = 1; i <= tot; i++) sl[i] = N;
for(int i = 1; i <= m; i++){
cin >> a[i];
a[i]++;
team[i] = (i + B - 1) / B;
sl[team[i]] = min(sl[team[i]], i);
sr[team[i]] = i;
}
for(int i = 1; i <= tot; i++){
build(1, 1, n);
for(int j = sl[i]; j <= sr[i]; j++){
int p = query(1, 1, n, a[j]), q = query2(1, 1, n, a[j]);
if(p) change(1, 1, n, 1, p, 1);
if(q <= n) change(1, 1, n, q, n, -1);
}
record(1, 1, n, i);
for(int j = 1; j <= n; j++){
sum[i][s[i][j]]++;
}
for(int j = 1; j <= n; j++){
sum[i][j] += sum[i][j - 1];
}
}
int ans = 0;
for(int i = 1, t, l, r, p; i <= q; i++){
cin >> t >> l >> r >> p;
t = (t + ans) % 2;
l = (l + ans) % m;
r = (r + ans) % m;
p = (p + ans) % n;
if(l > r) swap(l, r);
l++, r++, p++;
if(t == 0){
ans = Query(l, r, p) - 1;
cout << ans << '\n';
}
else{
ans = Query2(l, r, p);
cout << ans << '\n';
}
}
return 0;
}