LOJ#6109. 「2017 山东二轮集训 Day4」增添
? 解题记录 ? ? LOJ ? ? Treap ? ? 可持久化数据结构 ?    2018-12-09 17:13:20    457    0    0

原题地址

感觉还是很妙一个题,自己想了一会,看标签后茅塞顿开。

发现这个题的操作一直在用数列已经有的部分去覆盖一部分。

因为一直在用原来的,有点可持久化的意思。

发现其实我只要维护一个数据结构,可以把原来的一段提取出来插入到一个位置,

并且可以维护区间加区间和以及可以可持久化即可,这里的可持久化是为了不影响原来已有的部分,对于所有的修改都在新点上操作。

这样的数据结构就只有可持久化平衡树了。

非旋Treap的可持久化和线段树差不多,只要有修改的地方拉一个新点起来就行了。

时间复杂度O(n log n),空间常数很大,注意不要爆空间。

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<ctime>
#define LL long long
using namespace std;
const int maxn = 1e5 + 5;
const int inf = 0x3f3f3f3f;
int n, m, o, l, r, x;
namespace Treap {
	struct node {
		int ch[2], sz, num, add;
		LL sum;
	} t[maxn * 350];
	const int L = 0, R = 1;
	int root, cnt;
	void push_up(int rt) {
		t[rt].sz = t[t[rt].ch[L]].sz + 1 + t[t[rt].ch[R]].sz;
		t[rt].sum = t[t[rt].ch[L]].sum + t[rt].num + t[t[rt].ch[R]].sum;
	}
	void push_down(int rt) {
		if(t[rt].add) {
			int &tl = t[rt].ch[L];
			if(tl) {
				t[++cnt] = t[tl], tl = cnt;
				t[tl].sum += t[tl].sz * t[rt].add;
				t[tl].num += t[rt].add;
				t[tl].add += t[rt].add;
			}
			int &tr = t[rt].ch[R];
			if(tr) {
				t[++cnt] = t[tr], tr = cnt;
				t[tr].sum += t[tr].sz * t[rt].add;
				t[tr].num += t[rt].add;
				t[tr].add += t[rt].add;
			}
			t[rt].add = 0;
		}
	}
	int Nmerge(int a, int b) {
		if(!a || !b) return a + b;
		if(rand() % (t[a].sz + t[b].sz) < t[a].sz) {
			t[a].ch[R] = Nmerge(t[a].ch[R], b);
			return push_up(a), a;
		} else {
			t[b].ch[L] = Nmerge(a, t[b].ch[L]);
			return push_up(b), b;
		}
	}
	int merge(int a, int b) {
		if(!a || !b) return a + b;
		if(rand() % (t[a].sz + t[b].sz) < t[a].sz) {
			push_down(a);
			t[++cnt] = t[a], a = cnt;
			t[a].ch[R] = merge(t[a].ch[R], b);
			return push_up(a), a;
		} else {
			push_down(b);
			t[++cnt] = t[b], b = cnt;
			t[b].ch[L] = merge(a, t[b].ch[L]);
			return push_up(b), b;
		}
	}
	void push_back(int x) {
		t[++cnt].sz = 1, t[cnt].sum = t[cnt].num = x;
		root = Nmerge(root, cnt);
		return ;
	}
	void split(int &a, int &b, int k) {
		push_down(a);
		if(t[a].sz == k) {b = 0; return ;}
		if(t[t[a].ch[L]].sz >= k) {
			t[++cnt] = t[a], a = cnt;
			split(t[a].ch[L], b, k);
			swap(t[a].ch[L], b);
			swap(a, b), push_up(b); // 现在重新接上的是右边的 
		} else {
			t[++cnt] = t[a], a = cnt;
			split(t[a].ch[R], b, k - t[t[a].ch[L]].sz - 1);
			push_up(a);
		}
	}
	void add(int l, int r, int dt) {
		int M = 0, L = root, R = 0;
		split(L, R, r + 1);
		split(L, M, l);
		t[M].add += dt;
		t[M].num += dt;
		t[M].sum += dt * t[M].sz;
		L = merge(L, M);
		L = merge(L, R);
		root = L;
	}
	LL query(int l, int r) {
		int lrt = root;
		int M = 0, L = root, R = 0;
		split(L, R, r + 1);
		split(L, M, l);
		return root = lrt, t[M].sum;
	}
	void replace(int l, int r, int x) {
		int lrt = root, nrt; 
		int M = 0, L = root, R = 0;
		split(L, R, l + x + 1);
		split(L, M, l), nrt = M;
		M = 0, L = lrt, R = 0;
		split(L, R, r + x + 1);
		split(L, M, r);
		L = merge(L, nrt);
		L = merge(L, R);
		root = L;
	}
}

int main() {
	using namespace Treap;
	//freopen("add10.in", "r", stdin);
	//freopen("my.out", "w", stdout);
	srand(66662333);
	scanf("%d%d", &n, &m);
	push_back(-inf);
	for(register int i = 1; i <= n; ++i)
		scanf("%d", &x), push_back(x);
	push_back(inf);
	for(register int i = 1; i <= m; ++i) {
		scanf("%d%d%d", &o, &l, &r);
		if(o == 1) scanf("%d", &x), add(l, r, x);
		else if(o == 2) scanf("%d", &x), replace(l, r, x);
		else printf("%lld\n", query(l, r));
	}
	return 0;
}



上一篇: HDU5608 function

下一篇: LOJ#2541. 「PKUWC2018」猎人杀

457 人读过
立即登录, 发表评论.
没有帐号? 立即注册
0 条评论
文档导航