题解 [联合省选 2020 A] 冰火战士(LOJ #3299 / 洛谷 P6619)【树状数组二分】

题目链接:洛谷 P6619 / LOJ #3299

题意

有两队人,分别为冰系和火系。每个人有所在队伍 t t t、温度 x x x、能量 y y y。多次添加、删除人,询问每次操作结束后尽可能大的 v v v,使得冰系所有温度不低于 v v v 的人的能量总和 w 1 w_1 w1 与火系所有温度不高于 v v v 的人的能量总和 w 2 w_2 w2 二者最小值最大,并输出 2 min ⁡ ( w 1 , w 2 ) 2\min(w_1,w_2) 2min(w1,w2)

题解

get 新技能:树状数组上二分。

首先把 x x x 离散化,并为冰系与火系的 y y y 都建树状数组,每次查询找到第一个冰系 y y y 的前缀和不小于火系 y y y 的后缀和的位置(稍微移一下项就是两个 y y y 的前缀和之和不超过所有火系 y y y 之和,边界特殊处理),注意各种奇怪的边界。

树状数组上二分的过程其实更像是倍增:从大到小枚举 2 i 2^i 2i,看当前位置再往后 2 i 2^i 2i 的和是否超出要求。显然当前位置在 2 i 2^i 2i 位及更低位是 0,因此加上 2 i 2^i 2i 后的 lowbit ⁡ \operatorname{lowbit} lowbit 就是 2 i 2^i 2i,对应的区间恰好是当前位置到 2 i 2^i 2i 个位置之后。

代码:

/**********
Author: WLBKR5
Problem: loj 3299, luogu 6619
Name: 冰火战士 
Source: 联合省选 2020 A卷 
Algorithm: 树状数组 
Date: 2020/06/24
Statue: accepted
Submission: loj.ac/submission/844420, www.luogu.com.cn/record/34619705
//Use O2, or it will TLE.
**********/
#include
using namespace std;
#ifdef ONLINE_JUDGE
const int SIZE=1<<22|1;char buf[SIZE+1],*p=buf+SIZE-1;
inline char gc(){ return (++p)!=buf+SIZE?*p:(fread(buf,1,SIZE,stdin),*(p=buf)); }
#else
#define gc getchar
#endif
inline int getint(){
	int ans=0;
	char c=gc();
	while(c<'0'||c>'9')
		c=gc();
	while(c>='0'&&c<='9'){
		ans=ans*10+c-'0';
		c=gc();
	}
	return ans;
}
inline void putint(int x){
	if(x>=10)putint(x/10);
	putchar('0'+x%10);
}
const int N=2e6+10;
struct query{
	int op,k,t,x,y;
} que[N];
int x[N],xcnt=0;

int ai[N],af[N];
int F[N];
inline void modifyi(int x,int val){ for(;x<=xcnt;x+=(x&-x))ai[x]+=val; }
inline void modifyf(int x,int val){ for(F[x]+=val;x<=xcnt;x+=(x&-x))af[x]+=val; }
inline int bs(int val,int &suml,int &sumr){
	int sum=val;suml=0,sumr=val;
	int pos=0;
	for(int i=20;i>=0;--i){
		if((pos|(1<<i))<=xcnt&&sum-ai[pos|(1<<i)]-af[pos|(1<<i)]+F[pos|(1<<i)]>0){
			sum-=ai[pos+(1<<i)]+af[pos+(1<<i)];
			suml+=ai[pos+(1<<i)];
			sumr-=af[pos+(1<<i)];
			pos|=1<<i;
		}
	}
	return pos+1;
}
inline int bs2(int val){
	int sum=0;
	int pos=0;
	for(int i=20;i>=0;--i){
		if((pos|(1<<i))<=xcnt&&sum+af[pos|(1<<i)]<=val){
			sum+=af[pos|(1<<i)];
			pos|=1<<i;
		}
	}
	return pos;
}

int main(){
	int q=getint();
	for(int i=0;i<q;i++){
		que[i].op=getint();
		if(que[i].op==1){
			que[i].t=getint(),que[i].x=getint(),que[i].y=getint();
			x[++xcnt]=que[i].x;
		}else que[i].k=getint()-1; 
	}
	sort(x+1,x+xcnt+1);
	xcnt=unique(x+1,x+xcnt+1)-x-1;
	for(int i=0;i<q;i++)
		if(que[i].op==1)que[i].x=lower_bound(x+1,x+xcnt+1,que[i].x)-x; 
	int sum_fire=0;
	for(int i=0;i<q;i++){
		if(que[i].op==1){
			//modify(que[i].x,que[i].y);
			if(que[i].t==1)sum_fire+=que[i].y,modifyf(que[i].x,que[i].y);
			else modifyi(que[i].x,que[i].y);
		}
		if(que[i].op==2){
			int k=que[i].k;
			//modify(que[k].x,-que[k].y);
			if(que[k].t==1)sum_fire-=que[k].y,modifyf(que[k].x,-que[k].y);
			else modifyi(que[k].x,-que[k].y);
		}
		int suml=0,sumr=0;
		int p=bs(sum_fire,suml,sumr);
		//cerr<<"> "<
		int ans=max(suml,sumr);
		if(!ans)puts("Peace");
		else{
			if(ans==sumr)
				putint(x[bs2(sum_fire-sumr)+1]),putchar(' '),putint(ans*2),putchar('\n');
			else
				putint(x[p-1]),putchar(' '),putint(ans*2),putchar('\n');
		}
	}
	return 0;
}

你可能感兴趣的:(题解,#,来源-各省省选,#,数据结构-树状数组)