【题解】 「联合省选2020」冰火战士 线段树+二分 LOJ3299

Legend

Link to LOJ

维护两个二元组集合 \(A,B\),支持操作 \(Q\ (1 \le Q \le 2\times10^6)\) 次:

  • 向其中某个集合插入一个二元组 \((t,e) \ (1 \le t \le 2\times 10^9,\sum e \le 2 \times 10^9)\)
  • 删除之前插入的一个二元组。

每次操作后都会询问:

  • 对于给定的 \(mid\) ,定义 \(Ans=\min\left(\sum\limits _{a\in A}[a.t\le mid]\times a.e,\sum\limits_{b\in B} [b.t \ge mid]\times b.e\right)\)

  • 请给出能取到最大的 \(Ans\times 2\) 的值 ,以及取到最大 \(Ans\) 时的最大 \(mid\)

时空 \(\textrm{3s/512MB}\)

Editorial

作为 联合省选\(2020\) 的第一题,必定是一道良心送温暖题,但大力的卡常似乎让它失去了应有的效果,让我们一起为出题人点踩。

wrong answer

我们把集合 \(A\) 里的二元组 \((t,e)\) 看成对一个数组 \(a\) 执行 \(a_t\gets a_t+e\)

我们把集合 \(B\) 里的二元组 \((t,e)\) 看成对一个数组 \(b\) 执行 \(b_t\gets b_t+e\)

因为不强制在线,显然可以先把 \(t\) 离散化到 \(O(Q)\) 级别。

那么题目实际上就是让我们取得数组 \(a\) 的一个前缀和 \(Sa\)\(b\) 的一个后缀和 \(Sb\),满足 \(a\) 最后一个数的下标小于等于 \(b\) 第一个数的下标,使得 \(\min(Sa,Sb)\) 尽可能大,在此前提下 \(mid\) 尽量大。

容易想到这个东西关于 \(mid\) 是个凸函数,我们尝试去三分最终温度。

可惜的是值域不是连续的,整数域上难以三分。

模拟退火,我觉得它过不了 \(\textrm{2e6}\) 的询问。

binary to ternary

先不考虑 \(mid\) 的计算,只考虑 \(Ans\)

考虑把三分变成二分,考虑到只有两个数组,发现 \(Ans\) 最大时一定满足这二者之一:

  • \(Sa\le Sb\),且 \(Sa\) 尽可能大。
  • \(Sa\ge Sb\),且 \(Sb\) 尽可能大。

这两种情况是可以直接二分的。

直接二分,还要维护前后缀和,复杂度 \(O(Q \log^2 Q)\)

改为使用线段树二分,复杂度 \(O(Q \log Q)\)

calculating \(mid\)

线段树二分我们可以顺带找到对应取得最大值的位置 \(mid'\),但它不一定是最大的那个。

对于 “\(Sa\le Sb\),且 \(Sa\) 尽可能大” 的情况,线段树二分的确可以直接满足 \(mid'=mid\),可以直接输出。

但对于第二类 “\(Sa\ge Sb\),且 \(Sb\) 尽可能大” 的情况,我们则需要找到 \(\ge mid'\) 的第一个 \(i\) 使得 \(b_i\not = 0\)。此时 \(mid=i\)。 这可以直接使用一个 \(\textrm{multiset}\) 实现。

Code

细节不是很多,常数却有很大。

\(\textrm{LOJ}\) 压时限过,在 \(\textrm{luogu}\) 怕是人没了。

Author : Imakf
#include <bits/stdc++.h>

#define LL long long

const int MX = 2e6 + 23;

using namespace std;

int read(){
	char k = getchar(); int x = 0;
	while(k < '0' || k > '9') k = getchar();
	while(k >= '0' && k <= '9')
		x = x * 10 + k - '0' ,k = getchar();
	return x;
}

struct Operation{
	int type ,t ,x ,y ,k ,id;
	void readSign(){type = 1 ,t = read() ,x = read() ,y = read();}
	void readWithdraw(){type = 2 ,k = read();}
	bool operator <(const Operation &B)const{return x < B.x;}
}O[MX];

bool cmp(Operation A ,Operation B){return A.id < B.id;}

struct node{
	int l ,r ,s[2];
	node *lch ,*rch;
}*root;

void pushup(node *x){
	x->s[0] = x->lch->s[0] + x->rch->s[0];
	x->s[1] = x->lch->s[1] + x->rch->s[1];
}

node *build(int l ,int r){
	node *x = new node; x->l = l ,x->r = r ,x->s[0] = x->s[1] = 0;
	if(l == r) x->lch = x->rch = nullptr;
	else{int mid = (l + r) >> 1;
		x->lch = build(l ,mid);
		x->rch = build(mid + 1 ,r);
		pushup(x);
	}return x;
}

void add(node *x ,int p ,int belong ,int v){
	if(x->l == x->r) return x->s[belong] += v ,void();
	if(p <= x->lch->r) add(x->lch ,p ,belong ,v);
	else               add(x->rch ,p ,belong ,v);
	return pushup(x);
}

#define pi pair<int ,int>
#define mp make_pair

bool operator <(pi a ,pi b){
	return a.first == b.first ? a.second < b.second : a.first < b.first;
}
pi max(pi a ,pi b){return a < b ? b : a;}

pi qsmall(node *x ,int lsum = 0 ,int rsum = 0){ // ice <= fire 的最后一个位置
	if(x->l == x->r) return mp(min(lsum + x->s[0] ,rsum + x->s[1]) ,x->l);
	int nlsum = lsum + x->lch->s[0] ,nrsum = rsum + x->rch->s[1];
	if(nlsum <= nrsum) return qsmall(x->rch ,nlsum ,rsum);
	return qsmall(x->lch ,lsum ,nrsum);
}

pi qbig(node *x ,int lsum = 0 ,int rsum = 0){ // ice >= fire 的第一个位置
	if(x->l == x->r) return mp(min(lsum + x->s[0] ,rsum + x->s[1]) ,x->l);
	int nlsum = lsum + x->lch->s[0] ,nrsum = rsum + x->rch->s[1];
	if(nlsum >= nrsum) return qbig(x->lch ,lsum ,nrsum);
	return qbig(x->rch ,nlsum ,rsum);
}

int lsh[MX] ,lshcnt;

int main(){
	int Q = read();
	for(int i = 1 ,op ; i <= Q ; ++i){
		op = read();
		if(op == 1) O[i].readSign();
		else O[i].readWithdraw();
		O[i].id = i;
	}
	sort(O + 1 ,O + 1 + Q);
	int lastx = -1;
	for(int i = 1 ; i <= Q ; ++i){
		if(O[i].type == 2) continue;
		if(O[i].x != lastx) lsh[++lshcnt] = O[i].x;
		lastx = O[i].x ,O[i].x = lshcnt;
	}
	sort(O + 1 ,O + 1 + Q ,cmp);
	root = build(1 ,lshcnt);

	multiset<int> soldier;
	soldier.insert(MX);

	for(int i = 1 ; i <= Q ; ++i){
		int p = i;
		if(O[p].type == 1){
			/*
			printf("Add type = \"%s\" ,temperature = %d ,energy = %d\n" 
					,O[p].t ? "fire" : "ice"
					,O[p].x
					,O[p].y);
			*/
			add(root ,O[p].x ,O[p].t ,O[p].y);
			if(O[p].t) soldier.insert(O[p].x);
		}
		else{
			p = O[p].k;
			add(root ,O[p].x ,O[p].t ,-O[p].y);
			if(O[p].t) soldier.erase(soldier.find(O[p].x));
		}
		
		pi vbig = qbig(root) ,vsm = qsmall(root);
		pi v = max(vbig ,vsm);
		
		// printf("Tmp pos = %d\n" ,lsh[v.second]);

		if(!v.first){
			puts("Peace");
			continue;
		}
	
		if(v.first == vsm.first && v.first != vbig.first){
			printf("%d %d\n" ,lsh[v.second] ,v.first * 2);
			continue;
		}	
		int pos = *soldier.lower_bound(v.second);
		
		if(v.first) printf("%d %d\n" ,lsh[pos] ,v.first * 2);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/imakf/p/13632146.html