树状数组+离散化+二分——[省选联考 2020 A/B 卷] 冰火战士

由于太蒻只能被吊打

problem(luogu链接 loj链接

假设战斗温度为\(k\),则对于每一个冰战士\(ice_i\)\(i\)战士能出战的条件为\(ice_i\leq k\),对于每一个火战士\(fire_i\)\(i\)战士能出战的条件是\(fire_i \geq k\),现在你需要找一个最适温度\(k\),使得\(\min(\sum{ice_i},\sum{fire_i})\)的两倍最大。

solution

可以知道,最终答案的温度一定是某个战士的温度(因为温度最适前提下越高越好),所以对数据进行离散化便于处理。

	sort(val+1,val+cnt_val+1);
	cnt_val=unique(val+1,val+cnt_val+1)-(val+1);
	for(int i=1;i<=m;i++)	e[i].x=lower_bound(val+1,val+cnt_val+1,e[i].x)-val;

我们定义两个函数\(ice(i)、fire(i)\),其中的每一个元素,\(ice(i)\)只升不降,\(fire(i)\)只降不升,即依题意战士顺序排序,在同一直角坐标系中,可以发现当两函数图像相交时有答案为最大值(不能理解就手动模拟),但实际上两函数图像不一定相交(或者交点不一定是整数),所以有两个可能的最适温度\(k\)一个是\(k_1=max(\sum ice_i<\sum fire_i)\)另一个是\(k_2=min(\sum ice_i \geq \sum fire_i)\)

那如何找 \(k\)呢,第一反应肯定是二分,我们已经有了每一个战士的温度,对于得到的这个数列\(k_i\)(从小到大排序过),将所有\(k_i \geq ice_i\)的价值加上\(ice_i\)的能量,所有\(k_i \leq fire_i\)的价值加上\(fire_i\)的能量。需要单点加,区间查询,考虑线段树或是树状数组。继续观察,发现\(k_2\)会等于\(k_1+1\),那我们需要一次二分求出\(k_1\),在将它加1得到\(k_2\),再一次计算得到\(k_2\)温度下对应的能量值,比较之后选较大,但是发现如果是\(k_2\),就有可能还有更大的\(k_2\)满足条件,则根据得到的最大能量值再求一个\(k_3\),取最大就好了。

一次查询可能需要三次二分求答案,用线段树的时间复杂度为\(O(n\log n)\),但是由于常数大可能过不去,那么考虑使用树状数组进行求解,树状数组上的二分(我认为)就是需要的加上不要的跳过,一直迭代即可。大概是这样的。

inline int find1(){
	int p=0,s=-delta_fire;
	for(int i=20;i>=0;i--){
		if(p+(1<<i)>cnt_val)	continue;
		int ls=s+ice[p+(1<<i)]-fire[p+(1<<i)];
		if(ls<0){
			s=ls;
			p+=(1<<i);
		}
	}
	return p;
}

树状数组一般都是从一个点开始往后加,那对于\(fire_i\)需要加在前缀就记录一个偏移量\(detla\),在统计能量的时候先把它加上就好啦。

inline void add_fire(int pos,int energy){
	delta_fire+=energy;
	for(int i=pos+1;i<=cnt_val;i+=lowbit(i))
		fire[i]-=energy;
}

看不懂就理解一下\(code\)好了qwq

code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<utility>
using namespace std;
const int M=2e6+5;
struct node{
	int t,x,y;
}e[M];
int m,cnt_val,val[M],ice[M],fire[M],delta_fire;

inline int read(){
	char ch;
	int res,fl=1;
	while((ch=getchar())&&(ch<'0'||ch>'9'))
		if(ch=='-')	fl=-1;
	res=ch-'0';
	while((ch=getchar())&&ch>='0'&&ch<='9')
		res=res*10+ch-'0';
	return res*fl;
}

inline int lowbit(int x){	return x&(-x);}

inline void add_ice(int pos,int energy){
	for(int i=pos;i<=cnt_val;i+=lowbit(i))
		ice[i]+=energy;
}

inline void add_fire(int pos,int energy){
	delta_fire+=energy;
	for(int i=pos+1;i<=cnt_val;i+=lowbit(i))
		fire[i]-=energy;
}

inline int find1(){
	int p=0,s=-delta_fire;
	for(int i=20;i>=0;i--){
		if(p+(1<<i)>cnt_val)	continue;
		int ls=s+ice[p+(1<<i)]-fire[p+(1<<i)];
		if(ls<0){
			s=ls;
			p+=(1<<i);
		}
	}
	return p;
}

inline int query(int pos){
	int sum_ice=0,sum_fire=delta_fire;
	for(int p=pos;p;p-=lowbit(p)){
		sum_ice+=ice[p];
		sum_fire+=fire[p];
	}
	return min(sum_ice,sum_fire);
}

inline int find2(int num){
	int p=0,sum_ice=0,sum_fire=delta_fire;
	for(int i=20;i>=0;i--){
		if(p+(1<<i)>cnt_val)	continue;
		int now_ice=sum_ice+ice[p+(1<<i)];
		int now_fire=sum_fire+fire[p+(1<<i)];
		if(now_ice<now_fire){
			sum_ice=now_ice;
			sum_fire=now_fire;
			p+=(1<<i);
		}else	if(min(now_ice,now_fire)==num){
			sum_ice=now_ice;
			sum_fire=now_fire;
			p+=(1<<i);
		}
	}
	return p;
}

int main(){
#ifndef ONLINE_JUDGE
	freopen("icefire2.in","r",stdin);
	freopen("icefire.out","w",stdout);
#endif
	m=read();
	for(int i=1;i<=m;i++){
		int tp;
		tp=read();
		if(tp==1){
			e[i].t=read(),e[i].x=read(),e[i].y=read();
			val[++cnt_val]=e[i].x;
		}else{
			int k;k=read();
			e[i].x=e[k].x;
			e[i].t=e[k].t;
			e[i].y=-e[k].y;
		}
	}
	sort(val+1,val+cnt_val+1);
	cnt_val=unique(val+1,val+cnt_val+1)-(val+1);
	for(int i=1;i<=m;i++)	e[i].x=lower_bound(val+1,val+cnt_val+1,e[i].x)-val;
	for(int i=1;i<=m;i++){
		if(e[i].t==0)	add_ice(e[i].x,e[i].y);
		else	add_fire(e[i].x,e[i].y);
		int p1=find1();
		pair <int,int> res1=make_pair(-1,-1);
		if(p1>0)	res1=make_pair(query(p1),p1);
		pair <int,int> res2=make_pair(-1,-1);
		if(p1<cnt_val){
			int s2=query(p1+1);
			int p2=find2(s2);
			res2=make_pair(s2,p2);
		}
		pair <int,int> res=max(res1,res2);
		if(res.first==0)
			printf("Peace\n");
		else
			printf("%d %d\n",val[res.second],res.first*2);
	}
	return 0;
}

posted @ 2020-07-22 08:27  蒟蒻zyx_qwq  阅读(137)  评论(0编辑  收藏  举报