【洛谷P6619】冰火战士

题目

题目链接:https://www.luogu.com.cn/problem/P6619
一场比赛即将开始。
每位战士有两个属性:温度和能量,有两派战士:冰系战士的技能会对周围造成降温冰冻伤害,因而要求场地温度不低于他的自身温度才能参赛;火系战士的技能会对周围造成升温灼烧伤害,因而要求场地温度不高于他的自身温度才能参赛。
当场地温度确定时,双方能够参赛的战士分别排成一队。冰系战士按自身温度从低到高排序,火系战士按自身温度从高到低排序,温度相同时能量大的战士排在前面。首先,双方的第一位战士之间展开战斗,两位战士消耗相同的能量,能量少的战士将耗尽能量退出比赛,而能量有剩余的战士将继续和对方的下一位战士战斗(能量都耗尽则双方下一位战士之间展开战斗)。如此循环,直至某方战士队列为空,比赛结束。
你需要寻找最佳场地温度:使冰火双方消耗总能量最高的温度的最高值。
现在,比赛还处于报名阶段,目前还没有任何战士报名,接下来你将不断地收到报名信息和撤回信息。其中,报名信息包含报名战士的派系和两个属性,撤回信息包含要撤回的报名信息的序号。每当报名情况发生变化(即收到一条信息)时,你需要立即报出当前局面下的最佳场地温度,以及该场地温度下双方消耗的总能量之和是多少。若当前局面下无论何种温度都无法开展比赛(某一方没有战士能参赛),则只要输出 Peace

思路

考场权值线段树被卡了 /kk。
首先题意就是要你维护两个序列 \(a\)\(b\)。问你 \(k\) 取什么值时 \(a\) 中不大于 \(k\) 的元素和与 \(b\) 中不小于 \(k\) 的元素和之和最大。
显然可以二分 \(k\),然后取两个序列分别排序后的前缀与后缀和最接近时的 \(k\) 即可。注意当 \(k\) 所在的位置不是火法师的时候要一直往后移直到是火法师为止。

算法 1

可以考虑直接二分,然后利用树状数组维护两个序列的前缀和。后移的操作就直接暴力上 \(\operatorname{multiset}\) 即可。
时间复杂度 \(O(n\log^2 n)\),期望得分 \(60pts\)

算法 2

发现上述二分 + 树状数组可以直接用权值线段树上二分代替。而后半部分我们可以根据求出的适合的温度 \(k\),设火法师中温度不小于 \(k\) 的有 \(t\) 个,那么就在权值线段树上找到最大的且后面有不少于 \(t\) 个火法师的温度 \(k'\)。那么 \(k'\) 即为答案。
可以把后缀和转化为前缀和求。上述所有操作均可以在 \(O(n \log n)\) 的时间复杂度内求出。
由于此方法常数极大,所以期望得分依然是 \(60pts\)

算法 3

可以利用在树状数组上二分的黑科技来代替权值线段树(注意不等同与二分 + 树状数组)。时间复杂度 \(O(n\log n)\)。期望得分 \(100pts\)

代码

注意以下代码在洛谷要开 \(\operatorname{-O2}\) 才可以过。但是看到 \(95\%\) 在洛谷过了的神仙都开了 \(\operatorname{-O2}\),所以姑且算在 CCF 少爷机上过了吧。

#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
using namespace std;

const int N=2000010,M=(1<<21);
int Q,tot,sum,cnt,b[N];

struct Query
{
	int opt,t,x,y;
}ask[N];

inline int read()
{
	int d=0; char ch=getchar();
	while (!isdigit(ch)) ch=getchar();
	while (isdigit(ch)) d=(d<<3)+(d<<1)+ch-48,ch=getchar();
	return d;
}

struct BIT
{
	int c[3][M+1];
	
	inline void add(int x,int v,int id)
	{
		for (int i=x;i<=M;i+=i&-i)
			c[id][i]+=v;
	}
	
	inline int query(int x,int id)
	{
		int ans=0;
		for (int i=x;i;i-=i&-i)
			ans+=c[id][i];
		return ans;
	}
}bit;

inline int binary1()
{
	int l=1,r=M,mid,sum1=0,sum2=0;
	while (l<r)
	{
		mid=(l+r)>>1;
		if (sum1+bit.c[0][mid]<=sum-sum2-bit.c[1][mid])
		{
			sum1+=bit.c[0][mid]; sum2+=bit.c[1][mid];
			l=mid+1;
		}
		else r=mid;
	}
	return l;
}

inline int binary2(int k)
{
	int l=1,r=M,mid,sum=0;
	while (l<r)
	{
		mid=(l+r)>>1;
		if (sum+bit.c[2][mid]>=k) r=mid;
			else l=mid+1,sum+=bit.c[2][mid];
	}
	return l;
}

int main()
{
	Q=read();
	for (int i=1;i<=Q;i++)
	{
		ask[i].opt=read(); ask[i].t=read();
		if (ask[i].opt==1)
		{
			ask[i].x=read(); ask[i].y=read();
			b[++tot]=ask[i].x;
		}
	}
	sort(b+1,b+1+tot);
	tot=unique(b+1,b+1+tot)-b-1;
	for (int i=1;i<=Q;i++)
		if (ask[i].opt==1)
			ask[i].x=lower_bound(b+1,b+1+tot,ask[i].x)-b;
	for (int i=1;i<=Q;i++)
	{
		if (ask[i].opt==1)
		{
			bit.add(ask[i].x,ask[i].y,ask[i].t);
			if (ask[i].t)
			{
				sum+=ask[i].y; cnt++;
				bit.add(ask[i].x,1,2);
			}
		}
		else
		{
			int j=ask[i].t; 
			bit.add(ask[j].x,-ask[j].y,ask[j].t);
			if (ask[j].t)
			{
				sum-=ask[j].y; cnt--;
				bit.add(ask[j].x,-1,2);
			}
		}
		int p=binary1(),q=bit.query(p-1,2),k=binary2(q+1);
		int ans1=bit.query(k,0),ans2=sum-bit.query(k-1,1);
		if (ans1 && ans2)
			printf("%d %d\n",b[k],2*min(ans1,ans2));
		else
			printf("Peace\n");
	}
	return 0;
}
posted @ 2020-06-27 23:40  stoorz  阅读(166)  评论(0编辑  收藏  举报