LuoguP6619 省选联考 2020 A/B 卷 冰火战士(树状数组)

  是不是快一年没写代码了啊?省选送分题现在得做一年。有没有学上先不管了。

  容易发现总能量计算方式是两队被选出的参赛者各自能量和的min。显然随场地温度升高,冰队能量和单增,火队能量和单减,在两者最接近时总能量应最大。那么对冰队<=火队和冰队>=火队分别找到最大总能量及对应温度即可,可以二分+树状数组处理,log方T掉。

  由树状数组的原理可以得到在树状数组上二分的方法。比如要找到满足某个条件的最长前缀,可以按二进制位从高到低枚举,假设当前找到的最长前缀为x,如果加上当前二进制位1<<i后仍满足条件,前缀就可以增长至x+(1<<i),x+1~x+(1<<i)这段的信息就存在树状数组的[x+(1<<i)]中。于是用树状数组二分替换二分+树状数组即可。

  有一些细节大约可以参考代码,并不知道写得简不简便。

#include<bits/stdc++.h>
using namespace std;
#define N 2000010
#define inf 2000000000
int T,cnt,w[N],v[N],tree[2][N],tot;
struct data{int op,team,T,E;
}a[N];
int read()
{
	int x=0;char c=getchar();
	while (c<'0'||c>'9') c=getchar();
	while (c>='0'&&c<='9') x=x*10+c-48,c=getchar();
	return x;
}
void add(int team,int k,int x){while (k<=cnt) tree[team][k]+=x,k+=k&-k;}
int sum(int team,int k){int s=0;while (k) s+=tree[team][k],k-=k&-k;return s;}
int main()
{
	T=read();
	for (int i=1;i<=T;i++)
	{
		a[i].op=read();
		if (a[i].op==1) a[i].team=read(),w[++cnt]=a[i].T=read(),a[i].E=read();
		else a[i].team=read();
	}
	sort(w+1,w+cnt+1);
	int t=unique(w+1,w+cnt+1)-w-1;
	for (int i=1;i<=T;i++) if (a[i].op) a[i].T=lower_bound(w+1,w+t+1,a[i].T)-w+a[i].team;
	cnt=t;w[++cnt]=inf;
	//for (int i=1;i<=T;i++) cout<<a[i].T<<endl;
	for (int i=1;i<=T;i++)
	{
		if (a[i].op==1) add(a[i].team,a[i].T,a[i].E),tot+=a[i].team*a[i].E;
		else
		{
			int x=a[i].team;
			add(a[x].team,a[x].T,-a[x].E),tot-=a[x].team*a[x].E;
		}
		int x=0,u=0,v=tot;
		for (int j=20;j>=0;j--)
		if (x+(1<<j)<=cnt&&u+tree[0][x+(1<<j)]<=v-tree[1][x+(1<<j)])
		x+=1<<j,u+=tree[0][x],v-=tree[1][x];
		//cout<<w[x]<<' '<<u<<' '<<v<<endl;
		int t=tot-sum(1,x+1);
		if (t>=u)
		{
			u=t;x=0;int s=tot;
			for (int j=20;j>=0;j--)
			if (x+(1<<j)<=cnt&&s-tree[1][x+(1<<j)]>=t) x+=1<<j,s-=tree[1][x];
		}
		if (u==0) printf("Peace
");
		else printf("%d %d
",w[x],u*2);
	}
	return 0;
}