Search for a command to run...
Progressive hints first, then the full explanation and implementation when you're ready to cash out.
Review status
AI-generated and still unreviewed. Double-check the details before internalizing them.
Hints
Open only as much as you need to keep the solve alive.
Ignore the two movie dimensions after preprocessing. For user , define one number The whole process depends only on the multiset of these values.
If the values are sorted, the process is greedy: starting with , you can consume the next user iff their . So after consuming users, every consumed threshold must fit before it: for the sorted prefix.
Equivalently, if , then after the dust settles the answer is the smallest fixed point of : the smallest such that . If , more people are still eligible; if , nobody new can join.
For every possible threshold value , let The answer is the first with . Since only increases by frequencies while increases by , changes locally when one user's threshold changes.
Maintain the minimum value of on ranges with lazy segment tree updates. Changing one user from old threshold to new threshold adds to all for and adds to all for . Then descend the tree to find the first position whose segment minimum is .
First, compress the problem down to what actually matters.
For user , define
This is exactly how much current popularity the movie needs before user is willing to watch. After this, action and drama are gone. They were just a slightly fancier way of giving us thresholds.
So the process becomes:
The Key Process
Sort the thresholds:
The process is just scanning this sorted list. If the smallest remaining threshold is too large, nobody else can watch. If it is small enough, take them.
So we can consume user iff
After people have watched, popularity is , so the -th smallest threshold must be at most that.
But sorting after every update is obviously dead on arrival: are huge.
Turn It Into Counts
Let
If current popularity is , then exactly users have threshold at most .
If , then there is still at least one unwatched suitable user, so the process continues.
If , then all users with threshold at most have already watched, and nobody else is suitable. The process stops.
Therefore the answer is the smallest such that
Why smallest? Because we start from and only move upward. If , we jump forward. If still , we continue. The first fixed point is where the chain gets stuck.
Also, if everyone watches, answer is . Any threshold above can be treated as irrelevant for positions , because popularity never exceeds .
The Segment Tree Value
Define
We need the first where .
Important facts:
Now what happens when one user's threshold changes?
Suppose old threshold is and new threshold is .
Removing old decreases by for every . Adding new increases by for every .
So:
This is screaming lazy segment tree.
Finding The First Zero
The tree stores range minimum of .
Because is never negative on , checking whether a segment minimum is is exactly checking whether that segment contains a zero. Then we descend:
That gives the first zero, which is the answer.
Initialization
Start with
for all .
Then for every user threshold :
After this, each position contains
Each query updates one threshold, does two range additions, then returns the first zero.
Complexity
There are segment tree positions. Each update does range operations and one descent.
Total complexity:
with memory.
That fits easily. Not even close to needing weird hacks.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
struct SegTree {
int n;
vector<int> mn, lazy;
SegTree(int n = 0) : n(n), mn(4 * n + 4), lazy(4 * n + 4) {}
void build(int v, int l, int r) {
if (l == r) {
mn[v] = -l;
return;
}
int mid = (l + r) / 2;
build(v * 2, l, mid);
build(v * 2 + 1, mid + 1, r);
mn[v] = min(mn[v * 2], mn[v * 2 + 1]);
}
void apply(int v, int val) {
mn[v] += val;
lazy[v] += val;
}
void push(int v) {
if (lazy[v] == 0) return;
apply(v * 2, lazy[v]);
apply(v * 2 + 1, lazy[v]);
lazy[v] = 0;
}
void add(int v, int l, int r, int ql, int qr, int val) {
if (ql > r || qr < l) return;
if (ql <= l && r <= qr) {
apply(v, val);
return;
}
push(v);
int mid = (l + r) / 2;
add(v * 2, l, mid, ql, qr, val);
add(v * 2 + 1, mid + 1, r, ql, qr, val);
mn[v] = min(mn[v * 2], mn[v * 2 + 1]);
}
void add(int l, int r, int val) {
if (l > r) return;
add(1, 0, n - 1, l, r, val);
}
int first_zero(int v, int l, int r) {
if (l == r) return l;
push(v);
int mid = (l + r) / 2;
if (mn[v * 2] == 0) return first_zero(v * 2, l, mid);
return first_zero(v * 2 + 1, mid + 1, r);
}
int first_zero() {
return first_zero(1, 0, n - 1);
}
};
int main() {
setIO();
int ac, dr;
cin >> ac >> dr;
int n;
cin >> n;
vector<int> a(n), d(n), b(n);
for (int &x : a) cin >> x;
for (int &x : d) cin >> x;
auto need = [&](int aa, int dd) {
return max(aa - ac, 0) + max(dd - dr, 0);
};
SegTree seg(n + 1);
seg.build(1, 0, n);
for (int i = 0; i < n; i++) {
b[i] = need(a[i], d[i]);
if (b[i] <= n) seg.add(b[i], n, 1);
}
int m;
cin >> m;
while (m--) {
int k, na, nd;
cin >> k >> na >> nd;
--k;
if (b[k] <= n) seg.add(b[k], n, -1);
a[k] = na;
d[k] = nd;
b[k] = need(a[k], d[k]);
if (b[k] <= n) seg.add(b[k], n, 1);
cout << seg.first_zero() << '\n';
}
return 0;
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
struct SegTree {
int n;
vector<int> mn, lazy;
SegTree(int n = 0) : n(n), mn(4 * n + 4), lazy(4 * n + 4) {}
void build(int v, int l, int r) {
if (l == r) {
mn[v] = -l;
return;
}
int mid = (l + r) / 2;
build(v * 2, l, mid);
build(v * 2 + 1, mid + 1, r);
mn[v] = min(mn[v * 2], mn[v * 2 + 1]);
}
void apply(int v, int val) {
mn[v] += val;
lazy[v] += val;
}
void push(int v) {
if (lazy[v] == 0) return;
apply(v * 2, lazy[v]);
apply(v * 2 + 1, lazy[v]);
lazy[v] = 0;
}
void add(int v, int l, int r, int ql, int qr, int val) {
if (ql > r || qr < l) return;
if (ql <= l && r <= qr) {
apply(v, val);
return;
}
push(v);
int mid = (l + r) / 2;
add(v * 2, l, mid, ql, qr, val);
add(v * 2 + 1, mid + 1, r, ql, qr, val);
mn[v] = min(mn[v * 2], mn[v * 2 + 1]);
}
void add(int l, int r, int val) {
if (l > r) return;
add(1, 0, n - 1, l, r, val);
}
int first_zero(int v, int l, int r) {
if (l == r) return l;
push(v);
int mid = (l + r) / 2;
if (mn[v * 2] == 0) return first_zero(v * 2, l, mid);
return first_zero(v * 2 + 1, mid + 1, r);
}
int first_zero() {
return first_zero(1, 0, n - 1);
}
};
int main() {
setIO();
int ac, dr;
cin >> ac >> dr;
int n;
cin >> n;
vector<int> a(n), d(n), b(n);
for (int &x : a) cin >> x;
for (int &x : d) cin >> x;
auto need = [&](int aa, int dd) {
return max(aa - ac, 0) + max(dd - dr, 0);
};
SegTree seg(n + 1);
seg.build(1, 0, n);
for (int i = 0; i < n; i++) {
b[i] = need(a[i], d[i]);
if (b[i] <= n) seg.add(b[i], n, 1);
}
int m;
cin >> m;
while (m--) {
int k, na, nd;
cin >> k >> na >> nd;
--k;
if (b[k] <= n) seg.add(b[k], n, -1);
a[k] = na;
d[k] = nd;
b[k] = need(a[k], d[k]);
if (b[k] <= n) seg.add(b[k], n, 1);
cout << seg.first_zero() << '\n';
}
return 0;
}