ssk数据结构

线段树合并

就是合并两棵动态开点线段树的信息。

如果有个要合并的节点为空就不用往下合并了。感性理解复杂度是nlogn的。具体实现看代码:

inline node* merge(node *rta, node *rtb, int l, int r) {
	if (rta == NULL) return rtb;
	if (rtb == NULL) return rta;
	node *ret = new node;
	if (l >= r) {
		ret -> mx = rta -> mx + rtb -> mx;
		ret -> mxd = l;
		return ret;
	} ret -> left = merge(rta -> left, rtb -> left, l, l + r >> 1);
	ret -> right = merge(rta -> right, rtb -> right, (l + r >> 1) + 1, r);
	ret = update(ret);
	return ret;
}

感觉在树上用的比较多(把子树信息合并到父节点上)。或者问题转化为支持两个集合的并的时候也可以。

题:

luogu P4556

每次在树上一条链上投放某种物品。问最后每个节点上数量最多的物品是什么。

Sol

先改在问考虑先树上差分。最后自底向上线段树合并莽上去就行了。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 1e5 + 5;

int en = 0;

struct edge {
	int head, to, nxt;
} ed[N << 1];

inline void addedge(int from, int to) {
	ed[++en].to = to; ed[en].nxt = ed[from].head; ed[from].head = en;
}

int fa[N][20], dep[N];

inline void dfs(int now, int f) {
	fa[now][0] = f; dep[now] = dep[f] + 1;
	for (int i = 1; i < 20; ++i) fa[now][i] = fa[fa[now][i - 1]][i - 1];
	for (int i = ed[now].head; i; i = ed[i].nxt) {
		int v = ed[i].to;
		if (v == f) continue;
		dfs(v, now);
	}
}

inline int lca(int a, int b) {
	int u = a, v = b;
	if (dep[u] < dep[v]) swap(u, v);
	int t = dep[u] - dep[v];
	for (int i = 0; i < 20; ++i)
		if ((t >> i) & 1) u = fa[u][i];
	if (u == v) return u;
	for (int i = 19; ~i; --i)
		if (fa[u][i] != fa[v][i])
			u = fa[u][i], v = fa[v][i];
	return fa[u][0];
}

struct node {
	int mx, mxd;
	node *left = NULL;
	node *right = NULL;
	node() { mx = mxd = 0; }
} *root[N];

inline node* update(node *now) {
	if (now -> left == NULL && now -> right == NULL) return now;
	if (now -> left == NULL) { now -> mx = now -> right -> mx; now -> mxd = now -> right -> mxd; return now; }
	if (now -> right == NULL){ now -> mx = now -> left -> mx; now -> mxd = now -> left -> mxd; return now; }
	if (now -> left -> mx < now -> right -> mx) { now -> mx = now -> right -> mx; now -> mxd = now -> right -> mxd; return now; }
	if (now -> right -> mx <= now -> left -> mx) { now -> mx = now -> left -> mx; now -> mxd = now -> left -> mxd; return now; }
	return now;
}
	
inline node* insert(node *now, int l, int r, int q, int k) {
	if (now == NULL) {
		now = new node;
	}
	if (l >= r) {
		now -> mx += k;
		now -> mxd = q;
		return now;
	}
	if (q <= (l + r >> 1)) now -> left = insert(now -> left, l, l + r >> 1, q, k);
	else now -> right = insert(now -> right, (l + r >> 1) + 1, r, q, k);
	now = update(now);
	return now;
}

inline int query(node *now, int l, int r, int q) {
	if (now == NULL) return 0;
	if (l >= r)  return now -> mx;
	int mid = l + r >> 1;
	if (q <= mid) {
		if (now -> left == NULL) return 0;
		int t = query(now -> left, l, mid, q);
		return t;
	} else {
		if (now -> right = NULL) return 0;
		int t = query(now -> right, mid + 1, r, q);
		return t;
	}
}

inline node* merge(node *rta, node *rtb, int l, int r) {
	if (rta == NULL) return rtb;
	if (rtb == NULL) return rta;
	node *ret = new node;
	if (l >= r) {
		ret -> mx = rta -> mx + rtb -> mx;
		ret -> mxd = l;
		return ret;
	} ret -> left = merge(rta -> left, rtb -> left, l, l + r >> 1);
	ret -> right = merge(rta -> right, rtb -> right, (l + r >> 1) + 1, r);
	ret = update(ret);
	return ret;
}

int ans[N], n, m, a, b, x, y, z;

inline int read() {
	register int s = 0;
	register char ch = getchar();
	while (!isdigit(ch)) ch = getchar();
	while (isdigit(ch)) s = (s << 1) + (s << 3) + (ch & 15), ch = getchar();
	return s;
}

inline void calc(int now, int f) {
	for (int i = ed[now].head; i; i = ed[i].nxt) {
		int v = ed[i].to;
		if (v == f) continue;
		calc(v, now);
		root[now] = merge(root[now], root[v], 1, N - 5);
	} if (root[now] == NULL) ans[now] = 0;
	else ans[now] = root[now] -> mxd * (root[now] -> mx > 0);
}

int main() {
	n = read(); m = read();
	for (int i = 1; i < n; ++i) {
		a = read(); b = read();
		addedge(a, b); addedge(b, a);
		root[i] = NULL;
	} memset(fa, 0, sizeof fa); dfs(1, 0); root[n] = NULL;
	while (m--) {
		x = read(); y = read(); z = read();
		int L = lca(x, y);
		root[x] = insert(root[x], 1, N - 5, z, 1);
		root[y] = insert(root[y], 1, N - 5, z, 1);
		root[L] = insert(root[L], 1, N - 5, z, -1);
		if (fa[L][0] != 0) {
			L = fa[L][0];
			root[L] = insert(root[L], 1, N - 5, z, -1);
		}
	} calc(1, 0);
	for (int i = 1; i <= n; ++i) printf("%d\n", ans[i]);
	return 0;
}

P3224

给一个图,图上有点权。每次要么连一条边,要么问当前点能到的点权第k大的点。

Sol

并查集维护连通性,发现就是让我们合并两个集合/求集合中第k大元素,线段树合并板子。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 1e5 + 5;

struct node {
	int sum;
	node *left = NULL;
	node *right = NULL;	
	node() {
		left = NULL;
		right = NULL;
		sum = 0;
	}
};

int tag = 0;
node *root[N];

inline node* merge(node *a, node *b, int l, int r) {
	if (a == NULL) return b;
	if (b == NULL) return a;
	node *ret = new node;
	if (l >= r) {
		ret -> sum = a -> sum + b -> sum;
		return ret;
	} ret -> left = merge(a -> left, b -> left, l, l + r >> 1);
	ret -> right = merge(a -> right, b -> right, (l + r >> 1) + 1, r);
	ret -> sum = a -> sum + b -> sum;
	return ret;
}

inline node* insert(node *now, int l, int r, int q, int k) {
	if (now == NULL) {
		now = new node;
		//cout << l << ' ' << r << ' ' << now << endl;
	}
//	cout << l << ' ' << r << ' ' << q << ' ' << k << endl;
	now -> sum += k;
	if (l >= r) return now;
	if (q <= (l + r >> 1)) now -> left = insert(now -> left, l, l + r >> 1, q, k);
	else now -> right = insert(now -> right, (l + r >> 1) + 1, r, q, k);
	return now;
}

inline int query(node *now, int l, int r, int ql, int qr) {
	if (now == NULL) return 0;
	if (ql <= l && r <= qr) return now -> sum;
	int ret = 0; int mid = l + r >> 1;
	if (ql <= mid) ret = query(now -> left, l, mid, ql, qr);
	if (qr > mid) ret += query(now -> right, mid + 1, r, ql, qr);
	return ret;
}

inline int kth(node *now, int l, int r, int k) {
	if (l >= r) return l;
	if (now -> left != NULL && now -> left -> sum >= k) return kth(now -> left, l, l + r >> 1, k);
	if (now -> left != NULL) return kth(now -> right, (l + r >> 1) + 1, r, k - now -> left -> sum);
	return kth(now -> right, (l + r >> 1) + 1, r, k);
}

int f[N], n, m, u, v, p[N];

inline int find(int x) {
	return f[x] == x ? x : f[x] = find(f[x]);
}

inline int read() {
	register int s = 0;
	register char ch = getchar();
	while (!isdigit(ch)) ch = getchar();
	while (isdigit(ch)) s = (s << 1) + (s << 3) + (ch & 15), ch = getchar();
	return s;
}

inline char readchar() {
	register char ch = getchar();
	while (ch != 'B' && ch != 'Q') ch = getchar();
	return ch;
}

int main() {
	n = read(); m = read(); int x, y;
	for (int i = 1; i <= n; ++i) {
		p[x = read()] = i; f[i] = i;
		root[i] = insert(root[i], 1, n, x, 1);
	}
	while (m--) {
		x = read(); y = read();
		x = find(x); y = find(y);
		if (x == y) continue;
		f[y] = x;
		root[x] = merge(root[x], root[y], 1, n);
	} m = read(); char opt;
	while (m--) {
		opt = readchar(); x = read(); y = read();
		if (opt == 'B') {
			x = find(x); y = find(y);
			if (x == y) continue;
			f[y] = x;
			root[x] = merge(root[x], root[y], 1, n);
		} else {
			x = find(x);
			if (root[x] -> sum < y) puts("-1");
			else printf("%d\n", p[kth(root[x], 1, n, y)]);
		}
	} return 0;
}

kruskal 重构树

讲P4197 Peaks 的时候拓展的一种在线做法,快忘了,记一下。

用它的题目有主要特征:从某个点开始只能走权值小于(或大于)k的边...

以P4197 Peaks 为例子,每次问你某个点开始只能走权值小于等于x的边,到达的点中点权第k大的点。

首先要考虑把那些点弄出来。能走到,表明两点间所有路径上边权的最大值得最小值<=x。通过这一点想到了什么?最小生成树。

开始构建ksuskal重构树。重构树的叶节点相当于原图中的点。我们首先进行ksuskal算法。假设当前被选中的边为(u,v),f[x]表示点x在重构树中当前所在连通块的根,则新建一个节点。另它的两个儿子分别为f[u]和f[v],它的点权为这条边的边权。这棵树有和性质?

首先,深度越浅的点边权越大(因为kruskal时他们在后边才加入的),并且重构树中两点lca的权值相当于这两点之间所有路径上边权的最大值的最小值!考虑kruskal算法的原理,这个lCA的边权是这两个点第一次连通时连上的边,连得时间靠后,时当时最小生成树上最大的边,且此时在最小生成树上它们两个已经连通了。根据最小生成树的性质,最小生成树上两点路径中最大的边等于原图中两点间所有路径上的边权的最大值的最小值。

那么根据上面的性质,不难想到以下算法。假设要求点x走边权不超过v的边能到的所有点,我们在重构树上以x为起点向上倍增,直到倍增到深度最小的权值小于v的点。此时这个点的子树的所有叶节点就对应了原图中点x走边权不超过v的边能到的所有点。

这个找出来什么都好办。直接树上的dfs序+主席树/划分树维护即可。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <map>

using namespace std;

int n, m, q, t, h[200005], nxt, u, v, w, cnt = 0; 
map<int, int> mp, ts;

int sorted[200005], num[21][200005], val[21][200005];

//MST
int f[210005];

inline int find(int x) {
	return f[x] == x ? x : f[x] = find(f[x]);
}

//原图 
struct edge {
	int from, to, val;
}ed[500005];

inline bool cmp(edge a, edge b) {
	return a.val < b.val;
}

//kruskal重构树 

int rt;

struct nodde {
	int fa[20], val, siz, l;
	nodde() {
		siz = 0; l = 0x3f3f3f3f;
	}
	vector<int> nxt;
}nd[210005];

inline void addedge(int from, int to) {
	nd[from].nxt.push_back(to);
}

//kruskal

inline int min_(int a, int b) {
	return a < b ? a : b;
}

inline void kruskal() {
	cnt = n; //memset(f, 0, sizeof f);
//	cout << "edges in MST : " <<  endl;
	sort(ed + 1, ed + m + 1, cmp);
	for (int i = 1; i < 2100005; ++i) f[i] = i;
	int edgenum = 0;
	for (int i = 1; i <= m; ++i) {
		int x = find(ed[i].from), y = find(ed[i].to);
		if (x == y) continue;
	//	cout << ed[i].from << ' ' << ed[i].to << ' ' << ed[i].val << endl;
		++edgenum;
		nd[x].fa[0] = ++cnt;
		nd[y].fa[0] = cnt;
		f[x] = f[y] = cnt;
		nd[cnt].val = ed[i].val;
		addedge(cnt, x); addedge(cnt, y);
		if (edgenum == n - 1) {
			return ;
		}
	}
}

//dfsxv

int dfn[200005], nm = 0;

inline void dfs(int now) {
//	cout << "fa = " << nd[now].fa[0] << endl;
	for (int i = 1; i < 20; ++i)
		nd[now].fa[i] = nd[nd[now].fa[i - 1]].fa[i - 1];
	int flag = 1;
	if (now <= n) dfn[nd[now].l = ++nm] = now;
	for (vector<int> :: iterator it = nd[now].nxt.begin(); it != nd[now].nxt.end(); ++it) {
		if (*it == nd[now].fa[0]) continue;
		dfs(*it);nd[now].siz += nd[*it].siz; flag = 0;
		nd[now].l = min_(nd[*it].l, nd[now].l);
	} nd[now].siz += flag; //cout << "siz[" << now << "] = " << nd[now].siz << endl;
}

inline int findfa(int now, int x) {
//	cout << "jump! " << now << ' ' << x << "\n";
	int u = now;
	for (int i = 19; i >= 0; --i) {
		if (nd[nd[u].fa[i]].val > x) continue;
		u = nd[u].fa[i];//cout << u << endl;	
	} return u <= n ? -1 : u;
}

void build(int l, int r, int ceng) {
  	if (l == r) return ;
  	int mid = l + r >> 1, isame = mid - l + 1;
  	for (int i = l; i <= r; i++) if (val[ceng][i] < sorted[mid]) isame--;
  	int ln = l, rn = mid + 1;
  	for (int i = l; i <= r; i++) {
    	if (i == l) num[ceng][i] = 0;
    	else num[ceng][i] = num[ceng][i-1];
    	if (val[ceng][i] < sorted[mid] || val[ceng][i] == sorted[mid] && isame > 0) {
      		val[ceng + 1][ln++] = val[ceng][i];
      		num[ceng][i]++;
      		if(val[ceng][i] == sorted[mid]) isame--;
    	} else val[ceng+1][rn++]=val[ceng][i];
  } build(l, mid, ceng + 1); build(mid + 1, r, ceng + 1);
}


int look(int ceng, int sl, int sr, int l, int r, int k) {
  	if (sl == sr) return val[ceng][sl]; int ly;
  	if (l == sl) ly = 0; else ly = num[ceng][l - 1];
  	int tolef = num[ceng][r] - ly;
  	if (tolef >= k) return look(ceng + 1, sl, sl + sr >> 1, sl + ly, sl + num[ceng][r] - 1, k);
	int lr = (sl + sr >> 1) + 1 + (l - sl - ly);
	return look(ceng +1, (sl + sr) / 2 + 1, sr, lr, lr + r - l + 1 - tolef - 1, k - tolef);
}

int qans(int l, int r, int k) { return look(0, 1, n, l, r, k); }

int main() {
//	freopen("sample2.in", "r", stdin);
//	freopen("ans.ans", "w", stdout);
	scanf("%d%d%d", &n, &m, &q);
	for (int i = 1; i <= n; ++i) scanf("%d", &h[i]);
	for (int i = 1; i <= m; ++i)
		scanf("%d%d%d", &ed[i].from, &ed[i].to, &ed[i].val);
nd[0].val = 0x3f3f3f3f;	kruskal();  rt = find(1); dfs(rt);
	for (int i = 1; i <= n; ++i) sorted[i] = val[0][i] = dfn[i] = h[dfn[i]];
	sort(sorted + 1, sorted + n + 1); build(1, n, 0);
	int lastans = 0;
	while (q--) {
		scanf("%d%d%d", &u, &v, &w);
		u = findfa(u, v);//puts("fuck");
		if (u == -1) {
			puts("-1");
			lastans = 0;
			continue;
		}
		if (nd[u].siz < w) {
			puts("-1");
			lastans = 0;
			continue;
		}
		
		printf("%d\n", qans(nd[u].l, nd[u].l + nd[u].siz - 1, nd[u].siz - w + 1));
	}
	return 0;
}

模板题还有[NOI2018]规程(卡SPFA)

文艺平衡树

处理带有区间翻转的一些区间操作问题。

由于splay满足BST的性质,出奇的适合维护区间。我们就把splay当成一个区间树看,每次以下标为关键字插入。

翻转[l,r]的时候,首先把l splay到根,再把r+2 splay到l的右子树,则根的右子树的左子树代表的就是那一段区间。我们打上翻转标记。

pushdown就每次翻转左右儿子。

区间翻转的模板:

int ch[N][2], fa[N], siz[N], cnt[N], v[N], tag[N], root, nm = 0;

#define ls(x) ch[x][0]
#define rs(x) ch[x][1]
#define isRight(x) (x==ch[fa[x]][1])

inline int newNode(int x) { v[++nm] = x; cnt[nm] = siz[nm] = 1; return nm; }

inline void update(int x) { siz[x] = siz[ls(x)] + siz[rs(x)] + cnt[x]; }

inline void pushdown(int x) {
	if (tag[x]) {
		tag[ch[x][0]] ^= 1; tag[ch[x][1]] ^= 1;
		swap(ch[x][0], ch[x][1]);
		tag[x] = 0;
	}
}

inline void rotate(int x) {
	int f = fa[x]; int ff = fa[f], c = isRight(x);
	if (ff) ch[ff][isRight(f)] = x; fa[x] = ff;
	if (ch[x][c ^ 1]) fa[ch[x][c ^ 1]] = f; ch[f][c] = ch[x][c ^ 1]; fa[f] = x; ch[x][c ^ 1] = f;
	if (!fa[x]) root = x; update(f); update(x);
}

inline void splay(int x, int top = 0) {
	while (fa[x] != top) { int f = fa[x], ff = fa[fa[x]]; if (ff != top) (isRight(x) ^ isRight(f)) ? rotate(x) :rotate(f); rotate(x); } update(x);
}

inline int find(int x) {
	if (!root) return -1;
	int now = root, lst = 0, rt = 0;
	while (now) {
		pushdown(now);
		if (v[now] == x) {
			rt += siz[ls(now)];
			break ;
		} else if (v[now] > x) lst = now, now = ls(now);
		else lst = now, rt += cnt[now] + siz[ls(now)], now = rs(now);
	} splay(now ? now : lst); return rt;
}

inline void insert(int x) {
	if (!root) { root = newNode(x); return ; }
	find(x); if (v[root] == x) { ++cnt[root]; update(root); return ; }
	int now = root; root = newNode(x); int c = v[now] > x;
	ch[root][c] = now; fa[now] = root;
	if (ch[now][c ^ 1]) fa[ch[now][c ^ 1]] = root;
	ch[root][c ^ 1] = ch[now][c ^ 1]; ch[now][c ^ 1] = 0;
	return ;
}

inline int kth(int k) {
	int now = root;
	while (now) {
		pushdown(now);
		if (k > siz[ls(now)] + 1) {
			k -= siz[ls(now)] + 1;
			now = rs(now);
		} else if (k <= siz[ls(now)]) now = ls(now);
		else break;
	} return now;
}

inline void reverse(int l, int r) {
	l = kth(l); r = kth(2 + r);
	splay(l); splay(r, l);
	tag[ch[ch[root][1]][0]] ^= 1;
}

例题

区间翻转,区间最大子段和

Sol

思路就是线段树区间最大子段和套到splay上面。由于树形态的改变会影响信息,rotate里update的部分变了一下(因为之前只维护size)。看代码注意细节吧:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

typedef long long ll;

const int N = 200005;

inline ll max_(ll a, ll b) {
	return a > b ? a : b;
}

struct node {
	ll ls, rs, sum, mx;
	inline node() { ls = rs = sum = mx = 0; }
	inline node(ll L, ll R, ll S, ll M) : ls(L), rs(R), sum(S), mx(M) { }
} nd[N], bs[N];

inline node merge(node a, node b) {
	return node(max_(a.ls, a.sum + b.ls), max_(b.rs, a.rs + b.sum), a.sum + b.sum, max_(max_(a.mx, b.mx), a.rs + b.ls));
}

int ch[N][2], siz[N], cnt[N], tag[N], fa[N], v[N], nm = 0, root;

#define ls(x) ch[x][0]
#define rs(x) ch[x][1]
#define isRight(x) (x==ch[fa[x]][1])

inline void pushdown(int x) {
	if (tag[x]) {
		swap(nd[x].ls, nd[x].rs);
		swap(ls(x), rs(x));
		tag[ls(x)] ^= 1; tag[rs(x)] ^= 1;
		tag[x] = 0;
	}
}

inline void update(int x) {
	siz[x] = siz[ls(x)] + siz[rs(x)] + cnt[x];
	pushdown(ls(x)); pushdown(rs(x));
	nd[x] = merge(merge(nd[ls(x)], bs[x]), nd[rs(x)]); 
}

inline int newNode(int x, ll y) {
	v[++nm] = x; siz[nm] = cnt[nm] = 1; bs[nm] = nd[nm] = node(max_(y, 0), max_(y, 0), y, max_(y, 0));
	return nm;
}

inline void rotate(int x) {
	pushdown(x);
	int f = fa[x], ff = fa[fa[x]], c = isRight(x);
	if (ff) ch[ff][isRight(f)] = x; fa[x] = ff;
	ch[f][c] = ch[x][c ^ 1]; fa[ch[x][c ^ 1]] = f;
	ch[x][c ^ 1] = f; fa[f] = x;
	update(ff); update(f); update(x);
	if (fa[x] == 0) root = x;
}

inline void splay(int x, int top = 0) {
	while (fa[x] != top) {
		int ff = fa[fa[x]], f = fa[x];
		if (ff == top) { rotate(x); break; }
		rotate(x); rotate(x);
	} update(x);
}

inline int find(int x) {
	if (!root) return -1;
	int now = root, lst = 0, rt = 0;
	while (now) {
		if (v[now] == x) { rt += siz[ls(now)]; break; }
		if (v[now] > x) now = ls(lst = now);
		else rt += siz[ls(now)] + cnt[now], now = rs(lst = now);
	} splay(now ? now : lst); return rt;
}

inline void insert(int x, ll val) {
	if (!root) { root = newNode(x, val); return ;  } find(x);
	if (v[root] == x) { ++cnt[root]; update(root); return ; }
	int now = root; root = newNode(x, val);
	ch[root][0] = now; fa[fa[now] = root] = 0;
	if (ch[now][1]) fa[ch[now][1]] = root; ch[root][1] = ch[now][1]; ch[now][1] = 0;
	update(root); splay(now);
}

inline int kth(int k) {
	int now = root;
	while (now) {
		pushdown(now);
		if (k > siz[ls(now)] + 1) {
			k -= siz[ls(now)] + 1;
			now = rs(now);
		} else if (k <= siz[ls(now)]) now = ls(now);
		else return now;
	} return now;
}

inline void print(int now) {
	pushdown(now);
	if (ch[now][0]) print(ch[now][0]);
	if (v[now] != -0x7fffffff && v[now] != 0x7fffffff) cerr << bs[now].sum << ' ';
	if (ch[now][1]) print(ch[now][1]);
}


inline void reverse(int l, int r) {
	l = kth(l); r = kth(r + 2);
	splay(l); splay(r, l);
	tag[ch[ch[root][1]][0]] ^= 1;
}

inline ll query(int l, int r) {
	l = kth(l); r = kth(r + 2);
	splay(l); splay(r, l);
	return nd[ch[ch[root][1]][0]].mx;
} 

int n, m; ll q;

inline int read() {
	register int s = 0, f = 1;
	register char ch = getchar();
	while (!isdigit(ch)) f = (ch == '-' ? -1 : 1), ch = getchar();
	while (isdigit(ch)) s = (s << 1) + (s << 3) + (ch & 15), ch = getchar();
	return s * f;
}

inline ll readll() {
	register ll s = 0, f = 1;
	register char ch = getchar();
	while (!isdigit(ch)) f = (ch == '-' ? -1 : 1), ch = getchar();
	while (isdigit(ch)) s = (s << 1) + (s << 3) + (ch & 15), ch = getchar();
	return s * f;
}

inline char readchar() {
	register char ch = getchar();
	while (ch != 'Q' && ch != 'M' && ch != 'R') ch = getchar();
	return ch;
}

int main() {
	freopen("girls.in", "r", stdin); freopen("girls.out", "w", stdout);
	n = read(); m = read();
	insert(-0x7fffffff, 0);
	for (int i = 1; i <= n; ++i) {
		q = readll();
		insert(i, q);
	} insert(0x7fffffff, 0);
	int l, r; char opt;
	while (m--) {
		opt = readchar(); l = read(); r = read();
		if (opt == 'R') reverse(l, r);//, print(root), cerr << endl;
		else if (opt == 'Q') printf("%lld\n", query(l, r));
		else if (opt == 'M') {
			l = kth(l + 1); bs[l] = node(max_(r, 0), max_(r, 0), r, max_(r, 0));
			update(l); splay(l);
		}
	} fclose(stdin); fclose(stdout);
	return 0;
}

还有些整体二分和cdq之类的东西,看了好多次了。

posted @ 2021-07-17 21:35  Smallbasic  阅读(104)  评论(0)    收藏  举报