[题解] CF85D Sum of Medians

前言

题目链接:洛谷

题目链接:CodeForces

码完之后去看题解,代码都好短……

终于没有用快读啦~

题意

对于一个初始为空的集合,有三种操作:

  1. add:向集合里加入数 \(x\) ,保证加入前集合中没有数 \(x\)
  2. del:从集合中删除数 \(x\) ,保证删除前集合中有 \(x\)
  3. sum:询问将集合里的数从小到大排序后,求下标 \(i\) 满足 \(5\)\(3\) 的数的和。

现有 \(n\) 次操作,对于每个查询操作,输出答案。

思路

看到插入、删除、区间操作而且不考虑序列的顺序,首先就想到了平衡树(splay)。

先来说 splay 中节点包含的信息:

  • \(wei[5]\) :当前节点排序后的下标对于 \(5\) 取模后为 \(wei\) 的下标,并存入该点的权值。简单来说, \(wei[j]\) 就是当前节点对于下标 \(i\) 满足 \(5\)\(j\) 的数的和的单点贡献。
  • \(sum[5]\) :当前节点排序后,子树中的 \(wei\) 的和。
  • 其他的就是平常的 splay 了,能打懒标记就行。

在向上 update 重塑节点信息的时候 :
\(sum_{pos}=sum_{ls} + sum_{rs} + wei_{pos}\)

首先对于 \(1\) 操作,二话不说首先 insert 。

应当考虑当前节点的贡献 \(wei\) 。贡献的大小 \(x\) 已知,只需要弄清楚权值对 \(1\) ~ \(5\) 哪一个 \(wei\) 做贡献的问题了。只需要查询 \(x\) 前驱的排名加 \(1\) 即可。

再是考虑插入 \(x\) 对于其他点的影响。可以发现,插入 \(x\) 对于大于 \(x\) 的数有影响,会使得这些点的 \(sum[i]\)\(wei[i]\) 变为 \(sum[(i+1)\%5]\)\(wei[(i+1)\%5]\) 。将权值为 \(x\) 的点 splay 到根节点,大于 \(x\) 的数全都在其右子树中了。然后只用修改右儿子的 \(wei\)\(sum\) ,对于右儿子美美地打上懒标记就行了。

然后是删除操作,现将他 splay 到根节点,把他的所有有关权值的信息清空了,避免对于之后的删除操作造成毁灭性的影响。

可以发现,删除 \(x\) 对于大于 \(x\) 的数有影响,会使得这些点的 \(sum[i]\)\(wei[i]\) 变为 \(sum[(i-1)\%5]\)\(wei[(i-1)\%5]\) 。接下来的操作就类似于操作1里面的操作了。

最后,就是最简单的查询操作了,直接访问根节点的信息就行了。

Code

时间复杂度为 \(O(5n\log n)\) ,最慢的点 \(1372ms\)

#include <cstdio>
#define INF 1e15
#define int long long
const int MAXN = 1e5 + 5;
struct Splay_Node {
	int fa, cnt, siz, val, tag, son[2], wei[5], sum[5];
	#define ls t[pos].son[0]
	#define rs t[pos].son[1]
};
struct Splay_Tree {
	Splay_Node t[MAXN];
	int root, tot, Top, stk[MAXN];
	bool Ident(int pos) {
		return t[t[pos].fa].son[1] == pos;
	}
	void New(int val, int fa) {
		t[++tot].fa = fa;
		t[tot].val = val;
		t[tot].cnt = t[tot].siz = 1;
	}
	void Build() {
		New(-INF, 0);
		root = tot;
		New(INF, root);
		t[root].son[1] = tot;
	}
	void Update(int pos) {
		for(int i = 0; i < 5; i++) t[pos].sum[i] = t[ls].sum[i] + t[rs].sum[i] + t[pos].wei[i];
		t[pos].siz = t[ls].siz + t[rs].siz + t[pos].cnt;
	}
	void Connect(int pos, int fa, int how) {
		t[pos].fa = fa;
		t[fa].son[how] = pos;
	}
	void Push_Down(int pos) {
		if(!t[pos].tag) return;
		int tmp[5];
		if(ls) {
			for(int i = 0; i < 5; i++) tmp[(i + t[pos].tag + 5) % 5] = t[ls].sum[i];
			for(int i = 0; i < 5; i++) t[ls].sum[i] = tmp[i];
			for(int i = 0; i < 5; i++) tmp[(i + t[pos].tag + 5) % 5] = t[ls].wei[i];
			for(int i = 0; i < 5; i++) t[ls].wei[i] = tmp[i];
			t[ls].tag = (t[ls].tag + t[pos].tag + 5) % 5;
		}
		if(rs) {
			for(int i = 0; i < 5; i++) tmp[(i + t[pos].tag + 5) % 5] = t[rs].sum[i];
			for(int i = 0; i < 5; i++) t[rs].sum[i] = tmp[i];
			for(int i = 0; i < 5; i++) tmp[(i + t[pos].tag + 5) % 5] = t[rs].wei[i];
			for(int i = 0; i < 5; i++) t[rs].wei[i] = tmp[i];
			t[rs].tag = (t[rs].tag + t[pos].tag + 5) % 5;
		}
		t[pos].tag = 0;
	}
	void Rotate(int pos) {
		int fa = t[pos].fa, grand = t[fa].fa;
		int how1 = Ident(pos), how2 = Ident(fa);
		Connect(pos, grand, how2);
		Connect(t[pos].son[how1 ^ 1], fa, how1);
		Connect(fa, pos, how1 ^ 1);
		Update(fa);
		Update(pos);
	}
	void Splay(int pos, int to) {
		int tmp = pos;
		Top = 0;
		stk[++Top] = tmp;
		while(tmp) stk[++Top] = tmp = t[tmp].fa;
		while(Top) Push_Down(stk[Top--]);
		for(int fa = t[pos].fa; t[pos].fa != to; Rotate(pos), fa = t[pos].fa)
			if(t[fa].fa != to) Ident(pos) == Ident(fa) ? Rotate(fa) : Rotate(pos);
		if(!to) root = pos;
	}
	int Find(int pos, int val) {
		if(!pos) return 0;
		Push_Down(pos);
		if(val == t[pos].val) return pos;
		else if(val < t[pos].val) return Find(ls, val);
		return Find(rs, val);
	}
	void Insert(int &pos, int val, int fa) {
		if(!pos) {
			New(val, fa);
			Splay(pos = tot, 0);
			return;
		}
		Push_Down(pos);
		if(val == t[pos].val) {
			t[pos].cnt++;
			Splay(pos, 0);
		}
		else if(val < t[pos].val) Insert(ls, val, pos);
		else Insert(rs, val, pos);
	}
	void Erase(int val) {
		int pos = Find(root, val);
		if(!pos) return;
		if(t[pos].cnt > 1) {
			t[pos].cnt--;
			Splay(pos, 0);
			return;
		}
		Splay(pos, 0);
		int l = ls, r = rs;
		while(t[l].son[1]) l = t[l].son[1];
		while(t[r].son[0]) r = t[r].son[0];
		Splay(l, 0);
		Splay(r, l);
		t[r].son[0] = 0;
	}
	int Query_Rank(int pos, int val) {
		if(!pos) return 0;
		if(val == t[pos].val) {
			int res = t[ls].siz + 1;
			Splay(pos, 0);
			return res;
		}
		else if(val < t[pos].val) return Query_Rank(ls, val);
		int res = t[ls].siz + t[pos].cnt;
		return Query_Rank(rs, val) + res;
	}
	int Query_Pre(int val) {
		int pos, res, newroot;
		pos = newroot = root;
		while(pos) {
			Push_Down(pos);
			if(t[pos].val < val) {
				res = t[pos].val;
				pos = rs;
			}
			else pos = ls;
		}
		Splay(newroot, 0);
		return res;
	}
};
Splay_Tree tree;
int n;
signed main() {
	tree.Build();
	scanf("%lld", &n);
	char opt[3];
	for(int i = 1, x; i <= n; i++) {
		scanf("%s", opt);
		if(opt[0] == 'a') {
			scanf("%lld", &x);
			tree.Insert(tree.root, x, 0);
			int pre = tree.Query_Pre(x);
			int pos = tree.Find(tree.root, x);
			int prerk = tree.Query_Rank(tree.root, pre);
			prerk %= 5;
			tree.Splay(pos, 0);
			tree.t[pos].wei[prerk] = tree.t[pos].sum[prerk] = x;
			int tmp[5];
			if(tree.rs) {
				for(int i = 0; i < 5; i++) tmp[(i + 1) % 5] = tree.t[tree.rs].sum[i];
				for(int i = 0; i < 5; i++) tree.t[tree.rs].sum[i] = tmp[i];
				for(int i = 0; i < 5; i++) tmp[(i + 1) % 5] = tree.t[tree.rs].wei[i];
				for(int i = 0; i < 5; i++) tree.t[tree.rs].wei[i] = tmp[i];
				tree.t[tree.rs].tag += tree.t[pos].tag;
			}
			tree.t[tree.rs].tag++;
			tree.Update(pos);
		}
		else if(opt[0] == 'd') {
			scanf("%lld", &x);
			int pos = tree.Find(tree.root, x);
			tree.Splay(pos, 0);
			int tmp[5];
			for(int i = 0; i < 5; i++) tree.t[pos].wei[i] = tree.t[pos].sum[i] = 0;
			if(tree.rs) {
				for(int i = 0; i < 5; i++) tmp[(i + 4) % 5] = tree.t[tree.rs].sum[i];
				for(int i = 0; i < 5; i++) tree.t[tree.rs].sum[i] = tmp[i];
				for(int i = 0; i < 5; i++) tmp[(i + 4) % 5] = tree.t[tree.rs].wei[i];
				for(int i = 0; i < 5; i++) tree.t[tree.rs].wei[i] = tmp[i];
				tree.t[tree.rs].tag += tree.t[pos].tag;
			}
			tree.t[tree.rs].tag--;
			tree.Erase(x);
		}
		else printf("%lld\n", tree.t[tree.root].sum[3]);
	}
	return 0;
}
posted @ 2021-07-12 16:17  Last_Breath  阅读(85)  评论(0编辑  收藏  举报