是不是快一年没写代码了啊?省选送分题现在得做一年。有没有学上先不管了。
容易发现总能量计算方式是两队被选出的参赛者各自能量和的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; }
LuoguP6619 省选联考 2020 A/B 卷 冰火战士(树状数组)
原文:https://www.cnblogs.com/Gloid/p/13290235.html