题解:[省选联考 2020 A/B 卷] 冰火战士

题目传送门

题意分析

\(\mathrm{ice}\) 表示冰系战士,\(\mathrm{fire}\) 表示火系战士。

\(\operatorname{ice}(t),\operatorname{fire}(t)\) 分别表示温度为 \(t\) 的时候,能出战的战士的能量总和。这很好用树状数组/线段树维护。就我一个 SB 在模拟赛上认为这是可持久化。

容易发现,双方消耗能量永远相同;因此温度为 \(t\) 时,消耗总能量为 \(2\min(\operatorname{ice}(t),\operatorname{fire}(t))\)

最终答案即求 \(\min(\operatorname{ice}(t),\operatorname{fire}(t))\) 最大时,\(t\) 的最大值。

根据冰系战士、火系战士的定义,可以发现 \(\operatorname{ice}(t)\)\(t\) 增大而不降\(\operatorname{fire}(t)\)\(t\) 增大而不增。因此 \(\min(\operatorname{ice}(t),\operatorname{fire}(t))\) 其实是单峰的(存在取值相同的部分),交点即 \(\operatorname{ice}(t)=\operatorname{fire}(t)\) 的点。

普通单峰函数是三分(其实我就写过一次三分),而存在取值相同也不能三分,考虑二分。

二分出一个 \(t=t_0\) 使得 \(\operatorname{ice}(t)<\operatorname{fire}(t)\) 成立且 \(t_0\) 最大。那么最大总能量一定在 \(t=t_0\)\(t=t_0+1\) 的时候取到。(函数是离散的,可能交点不在整点上,两个点都有可能)。

  • 若为 \(t=t_0\),则这就是答案,因为 \(t\) 再大的时候就不满足总能量最大。
  • 若为 \(t=t_0+1\) 的时候,还需要再次二分找到 \(t=t_0'\) 使得总能量相同,且 \(t_0'\) 最大。

温度需要离散化。


这样的复杂度为 \(\mathcal O\left(q\log^2q\right)\),考虑道时限 \(\text{3s}\),很可以做。

但是据说需要 \(\mathcal O(q\log q)\) 的做法,也很简单,就是倍增查询树状数组/线段树二分。直接去掉二分,充分利用已知信息。

AC 代码

懒,\(\mathcal O\left(q\log^2q\right)\) 写法。luogu 最慢 \(\text{1.83s}\)

//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
constexpr const int N=2e6;
struct operation{
	int op,t,x,y;
}a[N+1];
int n,len,tmp[N+1];
void discrete(){
	for(int i=1;i<=n;i++){
		if(a[i].op==1){
			tmp[++len]=a[i].x;
		}
	}
	sort(tmp+1,tmp+len+1);
	len=unique(tmp+1,tmp+len+1)-tmp-1;
	for(int i=1;i<=n;i++){
		if(a[i].op==1){
			a[i].x=lower_bound(tmp+1,tmp+len+1,a[i].x)-tmp;
		}
	}
}
struct bit{
	int t[N+1+1],sum;
	int lowbit(int x){
		return x&-x;
	}
	void add(int x,int k){
		sum+=k;
		while(x<=N){
			t[x]+=k;
			x+=lowbit(x);
		}
	}
	int query(int x){
		if(x<=0){
			return 0;
		}
		int ans=0;
		while(x){
			ans+=t[x];
			x-=lowbit(x);
		}
		return ans;
	}
}ice,fire;
int main(){
	/*freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);*/
	
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i].op;
		switch(a[i].op){
			case 1:
				cin>>a[i].t>>a[i].x>>a[i].y;
				break;
			case 2:
				cin>>a[i].x;
				break;
		}
	}
	discrete();
	for(int i=1;i<=n;i++){
		if(a[i].op==1){
			switch(a[i].t){
				case 0:
					ice.add(a[i].x,a[i].y);
					break;
				case 1:
					fire.add(a[i].x,a[i].y);
					break;
			}
		}else{
			switch(a[a[i].x].t){
				case 0:
					ice.add(a[a[i].x].x,-a[a[i].x].y);
					break;
				case 1:
					fire.add(a[a[i].x].x,-a[a[i].x].y);
					break;
			}
		}
		int l=1,r=len,t=0;
		while(l<=r){
			int mid=l+r>>1;
			if(ice.query(mid)<fire.sum-fire.query(mid-1)){
				t=mid;
				l=mid+1;
			}else{
				r=mid-1;
			}
		}
		int ans1=min(ice.query(t),fire.sum-fire.query(t-1));
		int ans2=min(ice.query(t+1),fire.sum-fire.query(t));
		if(ans1<=0&&ans2<=0){
			cout<<"Peace\n";
		}else if(ans1>ans2){
			cout<<tmp[t]<<' '<<(ans1<<1)<<'\n';
		}else{
			t++;
			int l=t,r=len;
			while(l<=r){
				int mid=l+r>>1;
				if(min(ice.query(mid),fire.sum-fire.query(mid-1))>=ans2){
					l=mid+1;
					t=mid;
				}else{
					r=mid-1;
				}
			}
			cout<<tmp[t]<<' '<<(ans2<<1)<<'\n';
		}
	}
	
	cout.flush();
	
	/*fclose(stdin);
	fclose(stdout);*/
	return 0;
}
posted @ 2026-02-02 22:45  TH911  阅读(1)  评论(0)    收藏  举报