LuoguP6619 省選聯考 2020 A/B 卷 冰火戰士(樹狀陣列)
阿新 • • 發佈:2020-07-12
是不是快一年沒寫程式碼了啊?省選送分題現在得做一年。有沒有學上先不管了。
容易發現總能量計算方式是兩隊被選出的參賽者各自能量和的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; }