CF1398E Two Types of Spells(平衡树)

一、关于 set

这题其实考场上想得差不多了,但是没时间写平衡树,又忘记 set 怎么用了,于是 gg,所以借此题总结一下 set 的用法。

  • 基本操作: se.insert(x); se.erase(x);\(x\) 是插入或删除的数值
  • 两个布尔型(后者“可以看作”):判断是否为空 se.empty(),判断元素 \(k\) 是否存在 se.count(k)
  • 两个常用指针:se.begin() 指头,加 * 取最小值;se.rbegin() 指尾,加 * 取最大值(注意不是 end());

要取出第 \(k\) 大?似乎不行。虽然可以用 set<int>::iterator 暴力跑,但是复杂度显然崩坏,只能手写或者 pbds 了。还有一些 size() clear() 什么的,不再赘述。

二、思路

看这道题:对于一个时刻,显然的思路是统计出闪咒的个数 \(k\),让前 \(k\) 大翻倍。注意,最小的闪咒一定不会被翻倍

运用反证法,如果它被翻倍,说明在它之前一定存在一个没有被翻倍的闪咒,则交换两者顺序能获得更优解 ——Wall_breaker

因为要支持 \(\mathcal{O(\log n)}\) 插入删除,所以要使用 set;因为要维护闪咒的个数和前 \(k\) 大,我们可以开三个 set:

  1. \(\text{Big}\) 维护当前前 \(k\) 大的咒语;
  2. \(\text{Small}\) 维护当前不是前 \(k\) 大且不是最小闪咒的咒语;
  3. \(\text{Power}\) 维护当前所有闪咒。

三、插入/删除

基本想法是先直接插直接删,最后再调整使其合法。

1.插入/删除

插入一个火咒 \(X\),直接扔进 \(\text{Small}\)

插入一个闪咒 \(X\),先和 \(\text{Power}\) 最小值 \(Y\) 比较一下,如果小于则说明 \(X\) 是最小闪咒,不能加入 \(\text{Small}\),反倒应该把 \(Y\) 插入;如果大于,则放心大胆加入 \(\text{Small}\),最后不要忘记在 \(\text{Power}\) 里也插入一个 \(X\)

删除一个火咒 \(X\),在 \(\text{Big}\)\(\text{Small}\) 里找一下,扔掉。

删除一个闪咒 \(X\),先和 \(\text{Power}\) 最小值 \(Y\) 比较一下,如果小于则说明 \(X\) 是最小闪咒,在 \(\text{Big}\)\(\text{Small}\) 里找不到,不用管;如果大于,则在它俩里面找一下,扔掉,最后同样不要忘记删掉 \(\text{Power}\) 里的那个 \(X\)

2.调整

如果 \(\text{Big}\) 元素个数少于 \(\vert \text{Power} \vert\)(即 \(\text{Power}\) 的元素个数),则从 \(\text{Small}\) 中拿最大值;如果多于 \(\vert \text{Power} \vert\),则把最小值扔进 \(\text{Small}\)。如果 \(\text{Big}\) 的最小值小于 \(\text{Small}\) 的最大值,则交换之。

到这里其实就基本做完了,统计答案可以开两个变量 \(sum,add\)\(sum\) 统计当前所有元素和,\(add\) 记录翻倍的元素和。前者可以每进来一个 \(d\) 就加一下,后者可以在每次修改 \(\text{Big,Small}\) 的时候记录。

下面是 AC 代码:

#define Max(x) (*x.rbegin())//返回集合元素最大/小值
#define Min(x) (*x.begin())
void main() {
	cin.tie(0),cout.tie(0);
	cin>>n;
	for(int i=1,tp,d; i<=n; ++i) {
		cin>>tp>>d;
		//修改部分
		sum+=d;
		if(d>0) {//加入元素
			if(tp==0) {//火咒
				Small.insert(d);
			} else {//闪咒
				++Power_cnt;//记录闪咒个数,便于调整时比较
				if(!Power.empty()) {
					if(d>Min(Power)) Small.insert(d);
					else Small.insert(Min(Power));
				}
				Power.insert(d);
			}
		} else {//删除元素
			d=-d;
			if(tp==0) {//火咒
				if(Big.count(d)) {
					Big.erase(d);
					add-=d;
				} else Small.erase(d);
			} else {//闪咒
				--Power_cnt;
				Power.erase(d);
				if(!Power.empty()) {
					if(d>Min(Power)) {
						if(Big.count(d)) {
							add-=d;
							Big.erase(d);
						} else Small.erase(d);
					} else {
						if(Big.count(Min(Power))) {
							add-=Min(Power);
							Big.erase(Min(Power));
						} else Small.erase(Min(Power));
					}
				}
			}
		}
		//调整部分
		while(Big.size()<Power_cnt&&!Small.empty()) {//少于|Power|
			add+=Max(Small);
			Big.insert(Max(Small));
			Small.erase(Max(Small));
		}
		while(Big.size()>Power_cnt) {//多于|Power|
			add-=(Min(Big));
			Small.insert(Min(Big));
			Big.erase(Min(Big));
		}
		while(!Big.empty()&&!Small.empty()) {//元素不合法
			if(Min(Big)<Max(Small)) {
				add+=Max(Small)-Min(Big);
				Big.insert(Max(Small));
				Small.erase(Max(Small));
				Small.insert(Min(Big));
				Big.erase(Min(Big));
			} else break;
		}
		cout<<sum+add<<endl;
	}
}

THE END

posted @ 2021-11-04 11:02  q0000000  阅读(35)  评论(0编辑  收藏  举报