zoukankan      html  css  js  c++  java
  • [题解] 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(5nlog 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
    ", tree.t[tree.root].sum[3]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    LeetCode_441. Arranging Coins
    LeetCode_437. Path Sum III
    Spearman秩相关系数和Pearson皮尔森相关系数
    Spark MLlib 之 Basic Statistics
    Spark MLlib 之 Naive Bayes
    Spark MLlib Data Type
    maven 下载 源码和javadoc 命令
    Hadoop的数据输入的源码解析
    Spark相关错误汇总
    Spark External Datasets
  • 原文地址:https://www.cnblogs.com/C202202chenkelin/p/15002494.html
Copyright © 2011-2022 走看看