[省选联考 2020 A/B 卷] 冰火战士

链接

P6619


分析

别看题目说了一大串,实际上就是要让你动态维护一个 \(x\) 满足 \(\min\{\sum\limits_{A_i\ge x}a_i,\sum\limits_{B_i\le x}b_i\}\) 最大,其中 \(A,B\) 是温度,\(a,b\) 是能量,总能量消耗就是最大值的两倍。

发现 \(\sum\limits_{A_i\ge x}a_i\) 是随 \(x\) 而单减的,而 \(\sum\limits_{B_i\le x}b_i\) 是随 \(x\) 而单增的。

容易想到一个 \(O(n\log^2 n)\) 的算法。
首先判断 Peace 只需要用线段树维护一下 \(A\) 的最大值和 \(B\) 的最小值即可。

先二分出第一个使得 \(\sum\limits_{A_i\ge x}a_i<\sum\limits_{B_i\le x}b_i\)\(x\),前一个 \(x'\)\(\sum\limits_{B_i\le x'}b_i\) 是一个可能的答案。同时找到 \(x\) 后的 \(\sum\limits_{A_i\ge x}a_i\) 也可能作为答案,所以再二分出最后一个 \(x''\) 使得 \(\sum\limits_{A_i\ge x}a_i=\sum\limits_{A_i\ge x''}a_i\),这样择优选择即可。

因为二分时需要查询前缀 \(B\) 和后缀 \(A\),我们可以维护两个树状数组,其中 \(A\) 的树状数组倒叙添加查询。

于是时间复杂度是两只 \(log\)。开了 O2 可以跑过 3s。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define in read()
inline int read(){
	int p=0,f=1;
	char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){p=p*10+c-'0';c=getchar();}
	return p*f;
}
const int N=2e6+5;
int q[N],qn,n;
struct ques{
	int opt,t,x,y;
}Q[N];
int c[N],s[N];
inline int lowbit(int x){return x&(-x);}
inline void updatec(int x,int d){for(int i=x;i<=qn;i+=lowbit(i))c[i]+=d;}
inline int queryc(int x){int res=0;for(int i=x;i>0;i-=lowbit(i))res+=c[i];return res;}
inline void updates(int x,int d){for(int i=x;i<=qn;i+=lowbit(i))s[i]+=d;}
inline int querys(int x){int res=0;for(int i=x;i>0;i-=lowbit(i))res+=s[i];return res;}
inline int bin(int x){
	int l=1,r=qn,mid;
	for(mid=(l+r)>>1;l<r;mid=(l+r)>>1)
		if(q[mid]<x)l=mid+1;
		else r=mid;
	return l;
}
int maxn[N<<2],minn[N<<2];
inline void changec(int l,int r,int p,int x,int d){
	if(l==r){minn[p]=d;return ;}
	int mid=(l+r)>>1;
	if(x<=mid)changec(l,mid,p<<1,x,d);
	else changec(mid+1,r,p<<1|1,x,d);
	minn[p]=min(minn[p<<1],minn[p<<1|1]);
}
inline void changes(int l,int r,int p,int x,int d){
	if(l==r){maxn[p]=d;return ;}
	int mid=(l+r)>>1;
	if(x<=mid)changes(l,mid,p<<1,x,d);
	else changes(mid+1,r,p<<1|1,x,d);
	maxn[p]=max(maxn[p<<1],maxn[p<<1|1]);
}
inline void solve(){
	//cout<<minn[1]<<' '<<maxn[1]<<'\n';
	if(minn[1]>maxn[1]){puts("Peace");return ;}
	int l=minn[1],r=maxn[1],mid=(l+r)>>1;
	while(l<r){
		mid=(l+r)>>1;
		if(querys(qn-mid+1)<queryc(mid))r=mid;
		else l=mid+1;
	}
	int ans1=0,ans2=0,tmp;
	if(querys(qn-l+1)<queryc(l)){
		if(l!=minn[1])ans1=q[l-1],ans2=queryc(l-1);
		tmp=querys(qn-l+1);
		if(!ans1||tmp>=ans2){
			r=maxn[1];
			for(r=maxn[1],mid=(l+r+1)>>1;l<r;mid=(l+r+1)>>1)
				if(querys(qn-mid+1)>=tmp)l=mid;
				else r=mid-1;
			ans1=q[l],ans2=tmp;
		}
	}
	else ans1=q[maxn[1]],ans2=queryc(maxn[1]);
	cout<<ans1<<' '<<2*ans2<<'\n';
}
signed main(){
	n=in;
	for(int i=1;i<=n;i++){
		Q[i].opt=in;
		if(Q[i].opt==1)Q[i].t=in,Q[i].x=in,Q[i].y=in,q[++qn]=Q[i].x;
		else Q[i].x=in;
	}
	sort(q+1,q+1+qn);
	qn=unique(q+1,q+1+qn)-(q+1);
	memset(minn,127,sizeof(minn));
	memset(maxn,128,sizeof(maxn));
	for(int i=1,opt,t,x;i<=n;i++){
		opt=Q[i].opt;
		if(opt==1){
			x=bin(Q[i].x);
			if(Q[i].t)updates(qn-x+1,Q[i].y),changes(1,qn,1,x,x);
			else updatec(x,Q[i].y),changec(1,qn,1,x,x);		
		}
		else{
			t=Q[i].x,x=bin(Q[t].x);
			if(Q[t].t)updates(qn-x+1,-Q[t].y),changes(1,qn,1,x,maxn[0]);
			else updatec(x,-Q[t].y),changec(1,qn,1,x,minn[0]);
		}
		solve();
	}
	return 0; 
}

这样二分答案再树状数组查询并不优秀,我们有一个更优秀的办法。

利用树状数组本身的性质在树状数组上二分,每次从大往小跳,累加树状数组路径来进行判断。发现倒叙树状数组不好做,所以两个树状数组都正序但 \(A\) 用全部减前缀来查询后缀,还要记录树状数组的单点值。

时间复杂度降为 \(O(n\log n)\)

然后发现还是跑不过,于是尝试丢掉线段树维护最大值最小值。幸运的是你可以丢掉它,根据输出可以发现只需要判断 \(ans2\) 是否为零即可判断 Peace。
然后发现在WOJ还是跑不过,所以再把离散化卡一卡。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define in read()
inline int read(){
	int p=0,f=1;
	char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){p=p*10+c-'0';c=getchar();}
	return p*f;
}
const int N=2e6+5;
struct llmmkk{
	int date,pos;
	friend bool operator<(const llmmkk &a,const llmmkk &b)
		{return a.date<b.date;}
}q[N];
int qn,n,bin[N],num[N];
struct ques{int opt,t,x,y;}Q[N];
int c[N],s[N],S[N],C[N];
inline int lowbit(int x){return x&(-x);}
inline void updatec(int x,int d){for(int i=x;i<=qn;i+=lowbit(i))c[i]+=d;C[x]+=d;}
inline int queryc(int x){int res=0;for(int i=x;i>0;i-=lowbit(i))res+=c[i];return res;}
inline void updates(int x,int d){for(int i=x;i<=qn;i+=lowbit(i))s[i]+=d;S[x]+=d;}
inline int querys(int x){int res=0;for(int i=x;i>0;i-=lowbit(i))res+=s[i];return res;}
int x2[22];
int res1,res2,now,tt,ans1,ans2,tmp,tp;
inline void solve(){
	res1=0,res2=0,now=0,tt=querys(qn),ans1=0,ans2=0;
	for(int i=tp;i>=0;i--){
		if(now+x2[i]>qn)continue;
		if(tt-res2-s[now+x2[i]]+S[now+x2[i]]>=res1+c[now+x2[i]])
			now+=x2[i],res1+=c[now],res2+=s[now];
	}
	ans1=num[now],ans2=res1,tmp=tt-res2,now++;
	if(tmp>=ans2){
		res2=0,now=0;
		for(int i=tp;i>=0;i--){
			if(now+x2[i]>qn)continue;
			if(tt-res2-s[now+x2[i]]+S[now+x2[i]]>=tmp)
				now+=x2[i],res2+=s[now];
		}
		ans1=num[now],ans2=tmp;
	}
	if(ans2)cout<<ans1<<' '<<(ans2<<1)<<'\n';
	else cout<<"Peace\n";
}
signed main(){
	n=in,x2[0]=1;
	for(int i=1;i<=21;i++)x2[i]=x2[i-1]<<1;
	for(int i=1;i<=n;i++){
		Q[i].opt=in,Q[i].t=in;
		if(Q[i].opt==1)Q[i].x=in,Q[i].y=in,q[++qn].date=Q[i].x,q[qn].pos=i;
	}
	sort(q+1,q+1+qn);
	for(int i=1;i<=qn;i++){
		if(q[i].date^q[i-1].date)num[++tmp]=q[i].date;
		bin[q[i].pos]=tmp;
	}
	while(tmp)tmp/=2,tp++;
	for(int i=1,t;i<=n;i++){
		if(Q[i].opt==1){
			if(Q[i].t)updates(bin[i],Q[i].y);
			else updatec(bin[i],Q[i].y);		
		}
		else{
			t=Q[i].t;
			if(Q[t].t)updates(bin[t],-Q[t].y);
			else updatec(bin[t],-Q[t].y);
		}
		solve();
	}
	return 0; 
}
posted @ 2022-02-18 18:47  llmmkk  阅读(67)  评论(0编辑  收藏  举报