[省选联考 2020 A/B 卷] 冰火战士
链接
分析
别看题目说了一大串,实际上就是要让你动态维护一个 \(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;
}