CF EducationalRound93 E.Two Types of Spells(权值线段树+堆维护最值)

题意
每个魔法都有自己的伤害,魔法1是加倍魔法,可以加倍后一个魔法的伤害,魔法0是普通魔法,给出q个更新,要求每次更新后计算魔法能造成的伤害最大值。
思路
设加倍魔法数量为k,权值线段树每次查询前k大的伤害和sumk(加倍多出来的伤害),mutiset维护魔法1和魔法2的最值,如果魔法1的最小值比魔法0的最大值大或不存在魔法0,sumk就要改变,因为至多加倍k-1个魔法1。
代码

#include
#define ll long long
#define LL long long
#define PB push_back
#define MP make_pair
using namespace std;
const int maxn=4e5+100;
const ll inf=1e18+10;
ll ID[maxn],ni,QT[maxn*4],C[maxn*4];
int n;
struct node{
	ll ty,d;
}q[maxn];
void Unique(){
	sort(ID+1,ID+1+n);
	ni=unique(ID+1,ID+n+1)-ID-1;
}
ll getid(ll x){
	return lower_bound(ID+1,ID+1+ni,x)-ID;
}
void add(ll o,ll l,ll r,ll x,ll v){
	if(l==r){
		QT[o]+=v;
		C[o]+=ID[x]*v;
	}
	else{
		ll mid=(l+r)>>1;
		if(x<=mid) add(o<<1,l,mid,x,v);
		else add((o<<1)|1,mid+1,r,x,v);
		QT[o]=QT[o<<1]+QT[(o<<1)|1];
		C[o]=C[o<<1]+C[(o<<1)|1];
	}
}
 
ll sumk(ll o,ll l,ll r,ll k){
	if(l==r){
		if(k>=QT[o])return C[o];
		else return C[o]/QT[o]*k;
	}
	else{
		ll mid=(l+r)>>1;
		ll sl=QT[o<<1],sr=QT[(o<<1)|1];
		if(k<=sr) return sumk((o<<1)|1,mid+1,r,k);
		else return C[(o<<1)|1]+sumk(o<<1,l,mid,k-sr);
	}
}
 
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%lld %lld",&q[i].ty,&q[i].d);
		ID[i]=abs(q[i].d);
	}
	Unique();
	int fire=0,light=0;
	multiset<ll,greater<ll>>fireSet;
    multiset<ll,less<ll>>lightSet;
	ll sum=0;
	for(int i=1;i<=n;i++){
		ll val=q[i].d;
		if(val>0)add(1,1,ni,getid(val),1);
		else add(1,1,ni,getid(-val),-1);
		if(q[i].ty){
			if(val>0)lightSet.insert(val),light++;
			else lightSet.erase(-val),light--;
		}
		else{
			if(val>0)fireSet.insert(val),fire++;
			else fireSet.erase(-val),fire--;
		}
		sum+=val;
		ll sumk0=sumk(1,1,ni,light);
		if(light&&fire){
			ll li=*lightSet.begin(),fi=*fireSet.begin();
			if(li>fi)sumk0+=fi-li;
		}
		else if(light&&!fire){
			ll li=*lightSet.begin();
			sumk0-=li;
		}
		printf("%lld\n",sum+sumk0);
	}
}

你可能感兴趣的:(线段树,堆)