結果
| 問題 |
No.3314 Library Rearrangement
|
| コンテスト | |
| ユーザー |
👑 |
| 提出日時 | 2025-04-17 07:13:32 |
| 言語 | C++23 (gcc 13.3.0 + boost 1.87.0) |
| 結果 |
AC
|
| 実行時間 | 707 ms / 3,000 ms |
| コード長 | 4,788 bytes |
| コンパイル時間 | 3,377 ms |
| コンパイル使用メモリ | 292,124 KB |
| 実行使用メモリ | 14,676 KB |
| 最終ジャッジ日時 | 2025-05-06 07:06:45 |
| 合計ジャッジ時間 | 21,010 ms |
|
ジャッジサーバーID (参考情報) |
judge4 / judge3 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 2 |
| other | AC * 39 |
ソースコード
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll INF = (ll)4e18;
struct Node {
ll sum; // 区間の和
ll minv; // 区間の最小値
ll second_min; // 区間内で 2 番目に小さい値
int cnt_min; // 区間内で最小値と等しい要素の個数
};
int N, K, Q;
vector<ll> A;
vector<tuple<int,int,ll>> updates;
vector<int> ql, qr;
vector<ll> qx;
vector<int> L, R; // 並列二分探索用左右端
vector<Node> seg;
// ノード v を子にプッシュダウン
void pushdown(int v) {
for(int c : {v<<1, v<<1|1}) {
if (seg[c].minv < seg[v].minv) {
// 葉でないなら,子の最小値を seg[v].minv に引き上げ
ll delta = seg[v].minv - seg[c].minv;
seg[c].sum += delta * seg[c].cnt_min;
seg[c].minv = seg[v].minv;
}
}
}
// 子から情報を引き上げ
void pull(int v) {
const Node &L = seg[v<<1], &R = seg[v<<1|1];
Node &U = seg[v];
U.sum = L.sum + R.sum;
if (L.minv < R.minv) {
U.minv = L.minv;
U.cnt_min = L.cnt_min;
U.second_min = min(L.second_min, R.minv);
} else if (L.minv > R.minv) {
U.minv = R.minv;
U.cnt_min = R.cnt_min;
U.second_min = min(L.minv, R.second_min);
} else {
U.minv = L.minv;
U.cnt_min = L.cnt_min + R.cnt_min;
U.second_min = min(L.second_min, R.second_min);
}
}
// 区間 [l..r] を走査し,A[i] = max(A[i], x) を行う
void range_chmax(int v, int l, int r, int ql, int qr, ll x) {
if (r < ql || qr < l || seg[v].minv >= x) return;
if (ql <= l && r <= qr && seg[v].second_min > x) {
// この区間中、最小値以外はすでに x 以上
ll delta = x - seg[v].minv;
seg[v].sum += delta * seg[v].cnt_min;
seg[v].minv = x;
return;
}
pushdown(v);
int m = (l + r) >> 1;
range_chmax(v<<1, l, m, ql, qr, x);
range_chmax(v<<1|1, m+1, r, ql, qr, x);
pull(v);
}
// 区間和を返す
ll query_sum(int v, int l, int r, int ql, int qr) {
if (r < ql || qr < l) return 0;
if (ql <= l && r <= qr) return seg[v].sum;
pushdown(v);
int m = (l + r) >> 1;
return query_sum(v<<1, l, m, ql, qr)
+ query_sum(v<<1|1, m+1, r, ql, qr);
}
// 初期木を構築
void build(int v, int l, int r) {
if (l == r) {
seg[v].sum = A[l];
seg[v].minv = A[l];
seg[v].second_min = INF;
seg[v].cnt_min = 1;
return;
}
int m = (l + r) >> 1;
build(v<<1, l, m);
build(v<<1|1, m+1, r);
pull(v);
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> N >> K >> Q;
A.resize(N);
for(int i = 0; i < N; i++) cin >> A[i];
updates.resize(K);
for(int i = 0; i < K; i++){
int l, r;
ll x;
cin >> l >> r >> x;
--l; --r;
updates[i] = make_tuple(l, r, x);
}
ql.resize(Q);
qr.resize(Q);
qx.resize(Q);
for(int i = 0; i < Q; i++){
cin >> ql[i] >> qr[i] >> qx[i];
--ql[i]; --qr[i];
}
// 並列二分探索の左右端を初期化
L.assign(Q, 0);
R.assign(Q, K+1);
// 初期の A で条件を満たすクエリは r=0 にする
vector<ll> pref(N+1, 0);
for(int i = 0; i < N; i++) pref[i+1] = pref[i] + A[i];
for(int i = 0; i < Q; i++){
ll s = pref[qr[i]+1] - pref[ql[i]];
if (s >= qx[i]) {
R[i] = 0;
}
}
seg.assign(4*N+4, Node());
// 並列二分探索 main loop
vector<vector<int>> bucket(K+2);
while (true) {
bool any = false;
for(int i = 1; i <= K; i++) bucket[i].clear();
for(int i = 0; i < Q; i++){
if (L[i] + 1 < R[i]){
int mid = (L[i] + R[i]) >> 1;
bucket[mid].push_back(i);
any = true;
}
}
if (!any) break;
// 木を構築
build(1, 0, N-1);
// updates を 1..K 順に適用しながら、bucket を処理
for(int i = 1; i <= K; i++){
auto [l, r, x] = updates[i-1];
range_chmax(1, 0, N-1, l, r, x);
for(int qi : bucket[i]){
ll s = query_sum(1, 0, N-1, ql[qi], qr[qi]);
if (s >= qx[qi]) R[qi] = i;
else L[qi] = i;
}
}
}
// 結果出力
// R[i] == 0 → 0 回目 (初めから)
// R[i] == K+1 → -1 (最後まで満たさない)
// それ以外 → R[i]
for(int i = 0; i < Q; i++){
if (R[i] == 0) cout << 0 << "\n";
else if (R[i] == K+1) cout << -1 << "\n";
else cout << R[i] << "\n";
}
return 0;
}