P9995 [Ynoi2000] rspcn 题解

发布时间 2023-12-29 16:22:50作者: JiaY19

思路

比较典的 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;
}