首页 > 其他分享 >P9995 [Ynoi2000] rspcn 题解

P9995 [Ynoi2000] rspcn 题解

时间:2023-12-29 16:37:43浏览次数:28  
标签:rt return val int 题解 Ynoi2000 rspcn id op

思路

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

标签:rt,return,val,int,题解,Ynoi2000,rspcn,id,op
From: https://www.cnblogs.com/JiaY19/p/17935165.html

相关文章

  • P9991 [Ynoi Easy Round 2023] TEST_107 题解
    思路题目即要求删除区间中的一个或多个颜色。考虑假如枚举删除颜色\(k\)。那么在\(l,r\)中的答案为:\[\max_{i=1}^{m+1}a_i-a_{i-1}\]其中\(a_i\)为颜色\(k\)在\(l\simr\)中的出现位置,\(a_{0}=l,a_{m+1}=r\)。可以分类讨论。答案为\(a_1-l\),那么只需要维护\(......
  • AT_abc020_c 题解
    链接(atcoder)链接(luogu)简单算法组合(?算法一爆搜,时间复杂度\(O(2^{n\timesm}\timest)\),不能通过此题。算法二考虑二分\(t\),然后暴搜,时间复杂度\(O(2^{n\timesm}\timeslog2(t))\),不能通过此题。算法三考虑二分\(t\),然后暴记忆化搜索,时间复杂度\(O(n\timesm......
  • CF1234F 题解
    blog。小清新题,下文\(V=20\)即值域。反转操作,本质就是选两个不相交连续段拼起来。显然合法的最终串长度一定\(\leV\)。将这些合法串预处理出来,那么每个串都对应一个「字母集合」。随便DP一下,求出所有集合中,的最大的合法「字母集合」大小。\(dp_{\smallU}\)就是只选一......
  • CF1917F Construct Tree 题解
    题目链接:https://codeforces.com/contest/1917/problem/F题意有\(n\)条长度\(l_i\)的边,问它们是否能组成一棵\(n+1\)个节点的树,使得树的直径长度为\(d\)。\(n,d\le2000\)。题解首先当然要存在一个边集\(D\),使得\(\sum\limits_{i\inD}l_i=d\),这可以使用背包......
  • 在不使用内置函数和中间变量的情况交换数字LeetCode力扣题解面试题16.01
    #异或法#Kotlin```KotlinclassSolution{  funswapNumbers(numbers:IntArray):IntArray{    numbers[0]=numbers[0]xornumbers[1]    numbers[1]=numbers[1]xornumbers[0]    numbers[0]=numbers[0]xor......
  • AT_joisc2015_h 题解
    传送门题意:给定长为\(n\)的字符串\(s\),你可以选择一个区间,将区间内的字符从小到大排序,求可以得到的最长回文子串长度,字符集大小为\(n\)。很有意思的题目。首先容易做到\(O(n^3)\)。考虑怎么优化。我们先考察排序的区间和回文区间的关系。如果两个区间无交,那么显然排序......
  • CF1835C Twin Clusters 题解
    题目链接点击打开链接题目解法很牛逼的构造题好像随也可以过长度为\(2^{k+1}\)的序列的不同区间有\(2^{2k+1}\)个,值域是\(4^k\),所以一定有解将\(a\)做一遍前缀和,那么问题转化成了找\(s_{r1}\opluss_{l1-1}=s_{r2}\opluss_{l2-1}\)先讲一讲具体做法:按照前\(k\)......
  • ICPC2021Kunming G Glass Bead Game 题解
    QuestionICPC2021KunmingGGlassBeadGame有\(n\)个玻璃珠,\(B_1,B_2,\cdots,B_n\)每一步你可以选择一个\(B_i\)移道第一个位置上,花费的代价为操作前\(B_i\)前面的玻璃珠的个数。已知每一步选择玻璃珠\(B_i\)的概率\(p_i\),问当\(m\rightarrow\infty\)时,在第\(......
  • ICPC2021Kunming G Find the Maximum 题解
    QuestionFindtheMaximum给出一个树,每个点有一个权值\(b_n\),求一条树上路径\(V\),要求\(\frac{\sum_{u\inV(-x^2+b_ux)}}{|V|}\)最大,其中\(x\)是自己选择的一个树Solution先转化一下\(\frac{\sum_{u\inV(-x^2+b_ux)}}{|V|}\),得到\[\frac{\sum_{u\inV(-x^2+b_......
  • vue前端node内存溢出问题解决
    前端项目运行时,如果经常运行慢,崩溃停止服务,报如下错误:FATALERROR:CALL_AND_RETRY_LASTAllocationfailed-JavaScriptheapoutofmemory(JavaScript堆内存不足) 原因:因为在Node中,通过JavaScript使用内存时只能使用部分内存(64位系统:1.4GB,32位系统:0.7GB),这个时候,如......