[Codeforces 992E] Nastya and King-Shamans
https://codeforces.com/contest/992/problem/E
题意:
给定序列,每次单点修改后,求是否有点 \(p\) 的权值等于 \(1\) ~ \(p - 1\) 的权值和。
思路:
设前缀和数组为 \(pre\) ,考虑将单点权值改为 \(a_i - pre[i - 1]\) ,然后只需要每次修改时维护一下,最后数个 \(0\) 。注意到若权值为正数,对前缀和的影响是一个斐波那契数列,所以修改后的权值 \(a'\) 中,正数的数量很少。利用这一性质,我们在线段树上二分即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int N = 2e5 + 7;
ll a[N], preSum[N];
struct Seg {
#define ls (id << 1)
#define rs (id << 1 | 1)
#define mid ((l + r) >> 1)
ll mx[N << 2], lz[N << 2];
void up(int id) {
mx[id] = max(mx[ls], mx[rs]);
}
void down(int id) {
if (!lz[id]) return;
lz[ls] += lz[id];
lz[rs] += lz[id];
mx[ls] += lz[id];
mx[rs] += lz[id];
lz[id] = 0;
return;
}
void build(int id, int l, int r) {
if (l == r) {
mx[id] = a[l] - preSum[l - 1];
return;
}
build(ls, l, mid);
build(rs, mid + 1, r);
up(id);
}
void modify(int id, int l, int r, int ql, int qr, ll v) {
if (l > qr || r < ql) return;
if (l >= ql && r <= qr) {
mx[id] += v;
lz[id] += v;
return;
}
down(id);
modify(ls, l, mid, ql, qr, v);
modify(rs, mid + 1, r, ql, qr , v);
up(id);
}
int query(int id, int l, int r) {
if (mx[id] < 0) return -1;
if (l == r) {
if (mx[id] == 0) return l;
else return -1;
}
down(id);
int pos = query(ls, l, mid);
if (pos == -1) pos = query(rs, mid + 1, r);
return pos;
}
}seg;
int n, m;
void solve() {
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
preSum[i] = a[i] + preSum[i - 1];
}
seg.build(1, 1, n);
int pos, v;
while (m--) {
cin >> pos >> v;
ll add = v - a[pos];
seg.modify(1, 1, n, pos, pos, add);
seg.modify(1, 1, n, pos + 1, n, -add);
a[pos] = v;
cout << seg.query(1, 1, n) << '\n';
}
}
int main() {
#ifndef stff577
ios::sync_with_stdio(false);
cin.tie(nullptr);cout.tie(nullptr);
cout << fixed << setprecision(20);
#endif
int t = 1;
while (t--) solve();
return 0;
}