【洛谷6619】[省选联考 2020 A/B 卷] 冰火战士(树状数组上二分)

点此看题面

  • 有冰火两种战士,每个战士有一个温度和一个能量值。一个冰族战士能出场当且仅当场地温度不低于它的温度,一个火族战士能出场当且仅当场地温度不高于它的温度。
  • 定义一种场地温度下的激烈程度为能同时出场的冰族战士能量值总和与火族战士能量值总和的较小值。
  • (n)次操作,每次加入或删除一个战士,求最大的激烈程度,以及激烈程度最大时最高的场地温度。
  • (nle2 imes10^6)

推导性质

发现,每种场地温度下能出场的冰族战士能量值总和相当于做了个前缀和,火族战士能量值总和相当于做了一个后缀和。

也就是说,随着场地温度的升高,冰族战士能量值总和必然递增,火族战士能量值总和必然递减。

则二者必然有一个交点,而这交点就是理论上的最大激烈程度。

具体实现容易想到线段树上二分去求出这个交点,然而这道题极度卡常,是过不了的。

树状数组上二分

也算是一个挺有趣的科技。

考虑我们强制二分的初始边界(l=1,r=2^k),由于树状数组上一个(v_i)维护的是([i-lowbit(i)+1,i])所有值的和,因此这样二分就能满足(v_{mid})维护的恰好是([l,mid])所有值的和。

对于这道题,方便起见我们把火族战士的前缀加改成全局加+后缀减,与冰族战士统一。

先线段树上二分一次求出满足冰族战士能量总和小于火族战士能量总和的最大位置,然后再二分出与下一位火族战士能量总和相同的最大位置。(也就是理论交点左右两侧的位置)

对于这两个位置的答案取一个较大值即可。

代码:(O(nlogn))

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 2000000
using namespace std;
int n,op[N+5],qx[N+5],qy[N+5],dc,dv[N+5];
namespace FastIO
{
	#define FS 100000
	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
	#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
	int OT;char c,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
	I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
	Tp I void read(Ty& x) {x=0;W(!isdigit(c=tc()));W(x=(x<<3)+(x<<1)+(c&15),isdigit(c=tc()));}
	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
	Tp I void write(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);}
	Tp I void write(Con Ty& x,Con char& y) {write(x),pc(y);}
	I void NA() {pc('P'),pc('e'),pc('a'),pc('c'),pc('e'),pc('
');}
}using namespace FastIO;
struct TreeArray
{
	#define S 2097152
	int tot,a[S+5],b[S+5];I void U(CI op,RI x,CI v) {if(!op) W(x<=S) a[x]+=v,x+=x&-x;else {tot+=v,++x;W(x<=S) b[x]-=v,x+=x&-x;}}//火族战士改成全局加+后缀减
	I int G1() {RI l=1,r=S,mid,t=-tot;W(l^r) mid=l+r>>1,mid<=dc&&t+a[mid]-b[mid]<=0?(t+=a[mid]-b[mid],l=mid+1):r=mid;return l-1;}//二分冰<火的最大位置
	I int G2(CI v) {RI l=1,r=S,mid,t=tot;W(l^r) mid=l+r>>1,mid<=dc&&t+b[mid]>=v?(t+=b[mid],l=mid+1):r=mid;return l-1;}//二分火=v的最大位置
	I int Q(int* p,RI x,RI t=0) {W(x) t+=p[x],x-=x&-x;return t;}I int Q(CI x) {return 2*min(Q(a,x),tot+Q(b,x));}//询问p数组第x位;询问x的答案
}T;
int main()
{
	RI i;for(read(n),i=1;i<=n;++i) read(op[i]),op[i]==1?(read(op[i],qx[i],qy[i]),dv[++dc]=qx[i]):(read(qx[i]),0);
	#define GV(x) (lower_bound(dv+1,dv+dc+1,x)-dv)
	RI x,y,u,v;for(sort(dv+1,dv+dc+1),dc=unique(dv+1,dv+dc+1)-dv-1,i=1;i<=n;++i)
	{
		op[i]^2?(qx[i]=GV(qx[i]),T.U(op[i],qx[i],qy[i])):T.U(op[qx[i]],qx[qx[i]],-qy[qx[i]]);//注意离散化
		x=T.G1(),y=T.G2(T.tot+T.Q(T.b,x+1)),(u=T.Q(x))<=(v=T.Q(y))&&(x=y,u=v),u?(write(dv[x],' '),write(u,'
')):NA();//两种情况比较
	}return clear(),0;
}
败得义无反顾,弱得一无是处
原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu6619.html