K - 商店街の区画選び / Choosing Blocks in a Shopping Street 解説
by
seekworser
平方分割の実装について
公式解説(kyopro_friends)さんの解説と同じ解法ですが、平方分割の実装について(この問題に限らず)より自分が書きやすいと思っている方法を紹介します。
一般に長さ \(B\) のバケットに分けられたデータ構造に対して \([l, r)\) クエリを処理する際には \(l\) が \(r\) 未満である間、以下のように処理を行うことにすると考えるとよいです。
- \(l \bmod B \equiv 0\) かつ \(l + B \le r\) の場合、バケット全体に処理を行い、\(l\) に \(B\) を加えます。
- そうではない場合、\(l\) 番目のインデックスに処理を行い \(l\) に \(1\) を加えます。
この実装の良い点として、全体を処理するべきバケットが何番目か、左端・右端へのはみ出しがあるかなどの細部に関わらず、現在の \(l\) の値のみによって処理を決定できるため、実装の際に混乱を招きにくいと考えています。 実装例を下記に示します。(43行目から52行目の while ループが上記の処理になります。)
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i, n) for (ll i=0; i<(n); i++)
#define all(a) a.begin(), a.end()
template<typename T> bool chmin(T &a, T b) {if (a > b){a = b; return true;} return false;}
template<typename T> bool chmax(T &a, T b) {if (a < b){a = b; return true;} return false;}
const ll INFL = (1LL << 61);
int main() {
ll n,k,q; cin >> n >> k >> q;
vector<ll> a(n), c(n);
rep(i, n) cin >> a[i];
rep(i, n) cin >> c[i];
vector<ll> acum(n+1, 0), ccum(n+1, 0);
rep(i, n) {
acum[i+1] = acum[i] + a[i];
ccum[i+1] = ccum[i] + c[i];
}
vector<ll> val(n - k + 1), cnt(n - k + 1);
ll cmax = 0;
rep(i, n - k + 1) {
val[i] = acum[i+k] - acum[i];
cnt[i] = ccum[i+k] - ccum[i];
chmax(cmax, cnt[i]);
}
ll b = 300;
ll block = (n - k + 1 + b - 1) / b;
vector<ll> block_cntmin(block, INFL);
rep(i, n - k + 1) chmin(block_cntmin[i / b], cnt[i]);
vector block_valmin(block, vector<ll>(b, INFL));
rep(i, n - k + 1) { chmin(block_valmin[i / b][cnt[i] - block_cntmin[i / b]], val[i]); }
rep(i, block) for (ll j=b-2; j>=0; j--) chmin(block_valmin[i][j], block_valmin[i][j+1]);
vector<ll> lazy(block, 0);
rep(i, q) {
ll t,x,y; cin >> t >> x >> y;
if (t == 1) {
--x;
vector<ll> rebuild_block;
ll l = max(0LL, x - k + 1);
ll r = min(n - k + 1, x + 1);
while (l < r) {
if (l % b == 0 && l + b <= r) {
lazy[l / b] += y - a[x];
l += b;
} else {
if (rebuild_block.size() == 0 || rebuild_block.back() != l / b) rebuild_block.emplace_back(l / b);
val[l] += y - a[x];
l++;
}
}
for (auto i : rebuild_block) {
block_valmin[i] = vector<ll>(b, INFL);
rep(j, b) {
if (i*b+j >= n - k + 1) break;
chmin(block_valmin[i][cnt[i*b+j] - block_cntmin[i]], val[i*b+j]);
}
for (ll j=b-2; j>=0; j--) chmin(block_valmin[i][j], block_valmin[i][j+1]);
}
a[x] = y;
} else {
if (cmax < x) {cout << "IMPOSSIBLE\n"; continue;}
ll ans = INFL;
rep(i, block) {
if (block_cntmin[i] + b <= x) continue;
ll idx = max(0LL, x - block_cntmin[i]);
chmin(ans, block_valmin[i][idx] + lazy[i]);
}
cout << ans << "\n";
}
}
}
投稿日時:
最終更新:
