P9995 [Ynoi2000] rspcn 题解
思路
比较典的 ODT 题目。
发现排序是一个非常有性质的操作。
它对区间的更改与颜色段均摊差不多。
那么我们可以想到用 ODT 来维护这一整个序列。
具体的,区间排序操作可以用 ODT 维护每次排序产生的段,每段用线段树维护排序后的结果。
每次修改就可以进行线段树的分裂与合并。
如何查询。
可以发现,我们所查询的是一段前缀的颜色数量。
那么只需维护每种颜色的最左出现位置。
可以在线段树上维护每个权值的出现次数和是否是最左出现。
另外再使用一个树状树组进行辅助修改,维护段的前缀答案。
查询时需要查完整的段上最左出现的值个数的前缀和,以及在查询切开的段的线段树上查前缀即可。
时间复杂度是一个常数巨大的单 \(\log\)。
空间复杂度相同。
Code
这份没有卡常的代码只能勉强跑过,最慢点要 \(7.9s\)。
#include <bits/stdc++.h>
using namespace std;
#define x first
#define y second
#define fro(i, x, y) for (int i = (x); i <= (y);i++)
#define pre(i, x, y) for (int i = (x); i >= (y);i--)
typedef pair<int, int> PII;
const int N = 1000010;
const int M = N * 32;
int n, m, a[N], t[N], rt[N], stk[N], ton[N], del[M];
int cnt, top, tot, lol, vis[N], siz[M], val[M], s[M][2];
struct Node
{
int l;
mutable int r, id;
inline bool operator<(const Node& other) const
{ return l < other.l; }
};
set<Node> q;
inline int lowbit(int x) { return x & (-x); }
inline void delet(int x) { del[++cnt] = x; }
inline int node()
{
int x = (cnt ? del[cnt--] : ++tot);
val[x] = siz[x] = s[x][0] = s[x][1] = 0;
return x;
}
inline int pode()
{
int x = (top ? stk[top--] : ++lol);
rt[x] = vis[x] = 0;
return x;
}
inline void add(int x, int k)
{ while (x <= n) t[x] += k, x += lowbit(x); }
inline int ask(int x)
{ int res = 0; while(x) res += t[x], x -= lowbit(x); return res; }
inline void push_up(int p)
{
siz[p] = siz[s[p][0]] + siz[s[p][1]];
val[p] = val[s[p][0]] + val[s[p][1]];
}
inline void update(int &p, int k, int l = 1, int r = n)
{
if(!p) p = node();
if (l == r)
{
siz[p]++, val[p] |= (ton[l] ^ 1), ton[l] |= 1;
return;
}
int mid = (l + r) / 2;
if(k <= mid)
update(s[p][0], k, l, mid);
else
update(s[p][1], k, mid + 1, r);
push_up(p);
}
inline int merge(int p1, int p2, int l = 1, int r = n)
{
if(!p1 || !p2) return p1 + p2;
if(l == r)
return siz[p1] += siz[p2], val[p1] |= val[p2], delet(p2), p1;
int mid = (l + r) / 2;
s[p1][0] = merge(s[p1][0], s[p2][0], l, mid);
s[p1][1] = merge(s[p1][1], s[p2][1], mid + 1, r);
return delet(p2), push_up(p1), p1;
}
inline void split(int &p1, int &p2, int op, int k, int l = 1, int r = n)
{
if(!p1) p1 = node();
if(l == r)
{
siz[p1] = k, siz[p2] -= k;
val[p1] = val[p2], val[p2] = 0;
if(siz[p2] == 0) delet(p2), p2 = 0;
return;
}
int mid = (l + r) / 2;
if(siz[s[p2][op]] <= k)
{
s[p1][op] = s[p2][op], k -= siz[s[p2][op]], s[p2][op] = 0;
if(k != 0)
{
if(op == 0) split(s[p1][!op], s[p2][!op], op, k, mid + 1, r);
else split(s[p1][!op], s[p2][!op], op, k, l, mid);
}
}
else
{
if(op == 0)
split(s[p1][op], s[p2][op], op, k, l, mid);
else
split(s[p1][op], s[p2][op], op, k, mid + 1, r);
}
push_up(p1), push_up(p2);
if (siz[p2] == 0)
delet(p2), p2 = 0;
}
inline auto split(int x)
{
if(x > n) return q.end();
auto it = --q.upper_bound({x, 0, 0});
if ((*it).l == x) return it;
int nw = pode(); add((*it).r, -val[rt[(*it).id]]);
swap(nw, (*it).id), q.insert({x, (*it).r, nw}), (*it).r = x - 1;
split(rt[(*it).id], rt[nw], vis[nw], (*it).r - (*it).l + 1);
vis[(*it).id] = vis[nw];
add((*it).r, val[rt[(*it).id]]), it++;
add((*it).r, val[rt[(*it).id]]);
return it;
}
inline void change(int l, int r, int op)
{
auto it1 = split(l);
auto it2 = split(r + 1);
int id = (*it1).id;
for (auto i = it1; i != it2; i++)
{
add((*i).r, -val[rt[(*i).id]]);
if (i != it1)
rt[id] = merge(rt[id], rt[(*i).id]), stk[++top] = (*i).id;
}
q.erase(it1, it2);
auto it = q.insert({l, r, id}).x;
add(r, val[rt[id]]), vis[id] = op;
}
inline int ask(int p, int k, int op, int l = 1, int r = n)
{
if (!p || !k) return 0;
if(l == r) return val[p];
int mid = (l + r) / 2, sum = 0;
if (k >= siz[s[p][op]])
{
k -= siz[s[p][op]];
if(op == 0)
return val[s[p][op]] + ask(s[p][!op], k, op, mid + 1, r);
return val[s[p][op]] + ask(s[p][!op], k, op, l, mid);
}
if(op == 0)
return ask(s[p][op], k, op, l, mid);
return ask(s[p][op], k, op, mid + 1, r);
}
signed main()
{
ios::sync_with_stdio(false), cin.tie(0);
cin >> n >> m;
fro(i, 1, n) cin >> a[i];
fro(i, 1, n)
{
++lol;
update(rt[lol], a[i]);
q.insert({i, i, lol});
add(i, val[rt[lol]]);
}
int lastans = 0;
fro(i, 1, m)
{
int l, r, c;
cin >> l >> r >> c;
l ^= lastans, r ^= lastans, c ^= lastans;
change(min(l, r), max(l, r), l >= r);
int s = (*(--q.upper_bound({c, 0, 0}))).l;
int x = (*(--q.upper_bound({c, 0, 0}))).id;
cout << (lastans = ask(rt[x], c - s + 1, vis[x]) + ask(c - 1)) << "\n";
}
return 0;
}