E - Best Performances 解説
by
physics0523
この問題には様々な解法がありますが、ここでは「同じデータ構造を つ使う」方針を使います。
今回はデータ構造として multiset(多重集合を管理するデータ構造) を用います。使う機能は以下の通りなので、同等の操作ができるならどれを使っても構いません。
- 多重集合を管理する。即ち、同一の値を複数保持することができる。
- 最大値(または最小値、またはその両方) にアクセスできる。
- 特定の値を つ削除することができる。
「大きい方から 個の値を管理する multiset 」「それ以外の値を管理する multiset 」「 の総和 」という つの要素を管理していくことを考えます。
最初 は大きい方から 個の値 (今回は 個の ) 、 は 内のそれ以外の値 (今回は 個の ) 、 は の総和と初期化します。
以下の操作 balance
, add
, erase
を定義します。これは つの multiset で実装できます。各操作を実装していく道中で も更新していきます。
balance
… の要素数が 個でないなら、 個になるまで の要素のうち最大のものを に移動することを繰り返す。そのうえで、 ( の最小値) ( の最大値) が満たされる限りこれらを入れ替えることを繰り返す。add
… に特定の値 を追加した上でbalance
する。erase
… 特定の値 を削除する。 に が含まれるならそれを消し、そうでないなら から を消す。その後balance
する。
(おまけ: 今回の操作では の要素数が を上回ることはないですが、 の要素数が を上回るような更新があるようなケースでも のうち小さい要素を に移動させれば上手く取り扱えます。)
を変更する時は以下の操作をかければよいです。
- 新しい を
add
する。 - その後、元の を
erase
する。
(この順番は逆でも良いですが と の大きさの合計が を下回るケースや の時に が双方空になるケースなどがあり実装がしづらくなります。なお、今回の問題では番兵として をたくさん入れるという対処法もあります。)
今回の問題では、 balance
内での swap の回数が であると見積もられるので、全体の計算量は となります。
この解法は他の問題にも応用できますが、実装上の注意として、操作の道中で や が空集合になったりする可能性があることに注意してください。
実装例 (C++):
#include<bits/stdc++.h>
using namespace std;
int k;
multiset<int> x,y;
long long s;
void balance(){
while(x.size()<k){
auto iy=y.end();iy--;
x.insert((*iy));
s+=(*iy);
y.erase(iy);
}
if(x.empty() || y.empty()){return;}
while(1){
auto ix=x.begin();
auto iy=y.end();iy--;
int ex=(*ix);
int ey=(*iy);
if(ex >= ey){break;}
s+=(ey-ex);
x.erase(ix);
y.erase(iy);
x.insert(ey);
y.insert(ex);
}
}
void add(int v){
y.insert(v);
balance();
}
void erase(int v){
auto ix=x.find(v);
if(ix!=x.end()){ s-=v; x.erase(ix); }
else{ y.erase(y.find(v)); }
balance();
}
int main(){
int n;
cin >> n >> k;
vector<int> a(n,0);
for(int i=0;i<k;i++){ x.insert(0); }
for(int i=k;i<n;i++){ y.insert(0); }
s=0;
int q;
cin >> q;
while(q>0){
q--;
int p,w;
cin >> p >> w;
p--;
add(w);
erase(a[p]);
a[p]=w;
cout << s << "\n";
}
return 0;
}
#include<bits/stdc++.h> using namespace std; int k; multiset<int> x,y; long long s; void balance(){ while(x.size()<k){ auto iy=y.end();iy--; x.insert((*iy)); s+=(*iy); y.erase(iy); } if(x.empty() || y.empty()){return;} while(1){ auto ix=x.begin(); auto iy=y.end();iy--; int ex=(*ix); int ey=(*iy); if(ex >= ey){break;} s+=(ey-ex); x.erase(ix); y.erase(iy); x.insert(ey); y.insert(ex); } } void add(int v){ y.insert(v); balance(); } void erase(int v){ auto ix=x.find(v); if(ix!=x.end()){ s-=v; x.erase(ix); } else{ y.erase(y.find(v)); } balance(); } int main(){ int n; cin >> n >> k; vector<int> a(n,0); for(int i=0;i<k;i++){ x.insert(0); } for(int i=k;i<n;i++){ y.insert(0); } s=0; int q; cin >> q; while(q>0){ q--; int p,w; cin >> p >> w; p--; add(w); erase(a[p]); a[p]=w; cout << s << "\n"; } return 0; }
投稿日時:
最終更新: