1. 程式人生 > 實用技巧 >LuoguP6619 省選聯考 2020 A/B 卷 冰火戰士(樹狀陣列)

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\n");
		else printf("%d %d\n",w[x],u*2);
	}
	return 0;
}