P6619 [省选联考 2020 AB 卷] 冰火战士
题面非常长,但是稍微思考一下发现就是求两边能量和的最小值的两倍。
容易想到离散化后用树状数组来维护每个温度上的能量情况(因为 \(n=2\times 10^6\),线段树解决常数太大会寄掉),那么只要解决查询就好了。
因为求的是最小值,而随着温度的上升,冰系战士能量和越来越大,火系战士能量和越来越小,所以找到它们差最小的地方就是答案了。
朴素做法是外面二分,里面套树状数组,但是两个 \(log\),又会死掉。
这时候我们学习了一个新知识:树状数组上二分!
根据树状数组的定义,\(c[i]\) 维护的区间是 \([i-lowbit(i)+1,i]\),所以只要像倍增一样一直跳,跳的过程中加上信息就好了。
#include<bits/stdc++.h>
using namespace std;
const int N=2e6+10;
struct node{
int t,x,y;
}id[N];
int T,tot,len,sum,a[N],b1[N],b2[N],c1[N],c2[N];
int lowbit(int x){return x&-x;}
void add(int c[N],int x,int k)
{
for(int i=x;i<=len;i+=lowbit(i))c[i]+=k;
return;
}
void query()
{
int p=0,s1=0,s2=0,k1=0,k2=0;
for(int i=25;i>=0;i--)
{
if(p+(1<<i)>len)continue;
if(s1+c1[p+(1<<i)]<=sum-(s2+c2[p+(1<<i)])+b2[p+(1<<i)])
{
s1+=c1[p+(1<<i)],s2+=c2[p+(1<<i)],p+=(1<<i);
}
}
if(sum==0||(p==len&&s1==0)||(s1==0&&s2==sum))printf("Peace\n");
else if(p==len)printf("%d %d\n",a[p],s1<<1);
else if(s1>sum-s2)printf("%d %d\n",a[p],s1<<1);
else
{
s1+=b1[p+1],p=0;
for(int i=25;i>=0;i--)
{
if(p+(1<<i)>len)continue;
if(k2+c2[p+(1<<i)]-b2[p+(1<<i)]<=s2)k1+=c1[p+(1<<i)],k2+=c2[p+(1<<i)],p+=(1<<i);
}
printf("%d %d\n",a[p],(sum-s2)<<1);
}
return;
}
int main()
{
scanf("%d",&T);
for(int i=1,op;i<=T;i++)
{
scanf("%d",&op);
if(op==1)scanf("%d%d%d",&id[i].t,&id[i].x,&id[i].y),a[++tot]=id[i].x;
else scanf("%d",&id[i].t);
}
sort(a+1,a+1+tot);
len=unique(a+1,a+1+tot)-a-1;
for(int i=1;i<=T;i++)if(id[i].x)id[i].x=lower_bound(a+1,a+1+len,id[i].x)-a;
for(int i=1;i<=T;i++)
{
if(id[i].x)
{
if(!id[i].t)add(c1,id[i].x,id[i].y),b1[id[i].x]+=id[i].y;
else add(c2,id[i].x,id[i].y),b2[id[i].x]+=id[i].y,sum+=id[i].y;
}
else
{
int k=id[i].t;
if(!id[k].t)add(c1,id[k].x,-id[k].y),b1[id[k].x]-=id[k].y;
else add(c2,id[k].x,-id[k].y),b2[id[k].x]-=id[k].y,sum-=id[k].y;
}
query();
}
return 0;
}

浙公网安备 33010602011771号