[codeforces534E]Listening to Music && 可持久化线段树

本文介绍了一种使用压缩技巧的线段树实现方法,通过将节点的多个值压缩到一个unsigned long long变量中来节省内存空间。这种方法适用于空间受限的情况,并展示了如何在更新和查询操作中正确维护这些压缩的值。
部署运行你感兴趣的模型镜像

每个线段树节点需要保存四个值,ls,rs,min,tag

由于空间不够 所以把他们压缩成一个unsinged long long

t[x] = (ls * N + rs) * T + val + tag

t[x] % T 即可得到val + tag, ls = t[x] / T / N, rs = t[x] / T % N

进行标记永久化过后可以用左右儿子的值解出自己的val,再解出tag

然后就卡着内存过去了

黑科技简直可怕

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<queue>
#define SF scanf
#define PF printf
#define mp make_pair
#define fir first
#define sec second
#define sqr(x) ((x)*(x))
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
typedef unsigned long long ull;
const int MAXN = 200000;
int n, m, rt[MAXN+10], Q;
pii a[MAXN+10];
struct Seg_Tree {
	static const int N = 8000000 + 10;
	static const int T = MAXN + 3;
	int ncnt;
	ull t[N];
	inline void calc_s(const bool &nl, const bool &nr, ull x, int &ls, int &rs, int &tag) {
		int s = x % T;
		ls = x / T / N; rs = x / T % N;
		tag = s - min((nl ? ls : t[ls] % T), (nr ? rs : t[rs] % T));
	}
	inline ull calc(const int &ls, const int &rs, const int &val) {
		return (1ull * ls * N + rs) * T + val;
	}
	void ins(const int &last, int &u, const int &L, const int &R, const int &l, const int &r) {
		if(L == R) {
			u = last+1;
			return ;
		}
		t[u = ++ncnt] = t[last];
		if(l <= L && R <= r) {
			t[u]++;
			return ;
		}
		int mid = (L+R) >> 1, ls, rs, val, lson = 0, rson = 0;
		calc_s((L == mid), (R == mid+1), t[u], ls, rs, val);
		if(l <= mid) ins(ls, lson, L, mid, l, r);
		if(r > mid) ins(rs, rson, mid+1, R, l, r);
		if(!lson) lson = ls;
		if(!rson) rson = rs;
		val += min(((L == mid) ? lson : t[lson] % T), ((R == mid+1) ? rson : t[rson] % T));
		t[u] = calc(lson, rson, val);
	}
	int query(const int &u, const int &L, const int &R, const int &l, const int &r) {
		if(L == R) return u;
		if(!u) return 0;
		if(l <= L && R <= r) return t[u] % T;
		int mid = (L+R) >> 1, ls, rs, val, ret = N;
		calc_s((L == mid), (mid+1 == R), t[u], ls, rs, val);
		if(l <= mid) ret = min(ret, query(ls, L, mid, l, r));
		if(r > mid) ret = min(ret, query(rs, mid+1, R, l, r));
		return ret + val;
	}
	inline void build() {
		for(int i = 1; i <= n; i++) 
			ins(rt[i-1], rt[i], 1, n-m+1, max(1, a[i].sec-m+1), a[i].sec);
	}
} seg;
int main() {
	SF("%d%d", &n, &m);
	for(int i = 1; i <= n; i++) SF("%d", &a[i].fir), a[i].sec = i;
	sort(a+1, a+1+n);
	seg.build();
	SF("%d", &Q);
	int ans = 0;
	for(int i = 1; i <= Q; i++) {
		int x, l, r;
		SF("%d%d%d", &l, &r, &x);
		x ^= ans;
		x = lower_bound(a+1, a+1+n, mp(x, 0)) - a;
		if(x == 1) ans = 0;
		else ans = seg.query(rt[x-1], 1, n-m+1, l, r);
		PF("%d\n", ans);
	}
	return 0;
}


您可能感兴趣的与本文相关的镜像

### 关于 CodeForces 892E 的解题思路分析 #### 使用可撤销并查集解决最小生成树中的边集合验证问题 针对给定的无向图以及多个询问,每个询问涉及一组特定的边,并要求判断这组边能否同时存在于某棵最小生成树中。此问题可以通过结合Kruskal算法构建最小生成树的过程来求解,在这一过程中利用到的是按照权重升序排列后的边逐步加入至森林结构之中[^1]。 为了高效处理多次查询而不影响后续操作的结果,引入了带有回溯功能的数据结构——即所谓的“可撤销并查集”。这种特殊形式的并查集允许执行合并(union)的同时记录下每一次变动以便之后能够恢复原状;当完成一次查询判定后即可通过一系列反向动作使数据结构回到初始状态,从而不影响其他独立事件的发生逻辑[^3]。 具体实现方法如下: - 将所有的边依据其权重从小到大排序; - 对每一个询问所涉及到的边也做同样的预处理; - 开始遍历已排序好的全局边列表,每当遇到属于当前待检验询问范围内的边时,则尝试将其纳入现有连通分量内; - 如果发现形成环路则说明该询问无法满足条件; - 同样地,任何不属于当前询问但同样处于相同权值下的其它边也应该被考虑进来以确保最终形成的MST是最优解的一部分; - 完成一轮测试后记得清除所有临时更改使得系统重置为未受干扰的状态准备迎接下一个挑战。 ```cpp #include <bits/stdc++.h> using namespace std; struct Edge { int u, v; }; class DSUWithRollback { public: vector<int> parent, rank, historyParent, historyRank; void init(int n){ parent.resize(n); iota(parent.begin(), parent.end(), 0); // Fill with identity mapping. rank.assign(n, 0); historyParent.clear(); historyRank.clear(); } int findSet(int i) {return (parent[i]==i)?i:(findSet(parent[i]));} bool isSameSet(int i, int j){ return findSet(i)==findSet(j);} void unionSets(int i, int j){ if (!isSameSet(i,j)){ historyParent.push_back(findSet(i)); historyParent.push_back(findSet(j)); historyRank.push_back(rank[findSet(i)]); historyRank.push_back(rank[findSet(j)]); int x=findSet(i), y=findSet(j); if (rank[x]>rank[y]) swap(x,y); parent[x]=y; if (rank[x]==rank[y]) ++rank[y]; } } void rollback(){ while(!historyParent.empty()){ parent[historyParent.back()]=historyParent.back(); historyParent.pop_back(); rank[historyParent.back()] = historyRank.back(); historyParent.pop_back(); historyRank.pop_back(); } } }; ``` 上述代码展示了如何创建一个支持撤销机制的并查集类`DSUWithRollback`,它可以在不破坏原有连接关系的前提下安全地进行节点间的联合与查找操作。此外还提供了用于追踪变化历史的方法,方便在必要时候撤消最近的一系列更动。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值