Problem Statement
Given people, lands and events.
Each land has an owner .
Given people, lands and events.
Each land has an owner .
Solve these external problems (e.g., from Codeforces) to reinforce the lesson material:
Each person has a target amount of resources .
Each event is described by and means that every land in the segment generates resources. If the segment wraps around, then the lands are considered on a circle.
So if a person owns lands inside the affected segment, then this person gains resources from that event.
For each person, we need to determine after which event they first reach their target amount. If they never reach it, the answer is -1.
We can solve the problem independently for each person.
For one fixed person , let mean whether person can reach the required amount using only the first events.
This function is monotone:
can(x, m) is true, then can(x, m+1) is also true.So for each person, we can binary search on the answer.
To evaluate can(x, m), we need to apply the first events. For that, we can use a Fenwick tree or a segment tree, together with helper functions like:
add_updates(l, r)remove_updates(l, r)Then we get the following solution for one person:
This works, but it is too slow, because the same updates are added and removed many times for different people.
The slow part is not checking can(x, m) itself, but repeatedly rebuilding the state for many different values of m.
The key observation is that many people ask questions about the same midpoint m.
So instead of running a separate binary search for each person, we process all people in parallel:
This is called parallel binary search.
We write a recursive function:
which means that the answer for every person in people lies in the interval .
If l == r, then the answer for all these people is already fixed.
Otherwise:
l to m;can(x, m) is true go to the left half;The advantage is that updates are reused across many people instead of being rebuilt from scratch for each person separately.
The same idea can also be implemented iteratively.
For each person , we maintain a current search range:
tl[x] — left boundary;tr[x] — right boundary.Initially, for every person:
tl[x] = 0tr[x] = K + 1Then we repeat the following process:
m, applying updates as we go;m, answer all people assigned to this midpoint;int solve(int x) {
int l = 0, r = K + 1;
while (l < r) {
int m = (l + r) / 2;
add_updates(1, m);
if (can(x, m)) {
r = m;
} else {
l = m + 1;
}
remove_updates(1, m);
}
if (l == K + 1) {
return -1;
}
return l;
}
solve(l, r, people)
void solve(int l, int r, vector<int> people) {
if (people.empty()) {
return;
}
if (l == r) {
for (int x : people) {
if (l == K + 1) {
res[x] = -1;
} else {
res[x] = l;
}
}
return;
}
int m = (l + r) / 2;
add_updates(l, m);
vector<int> left_people, right_people;
for (int x : people) {
if (can(x, m)) {
left_people.push_back(x);
} else {
right_people.push_back(x);
}
}
remove_updates(l, m);
solve(l, m, left_people);
solve(m + 1, r, right_people);
}
void solve() {
for (int i = 1; i <= n; i++) {
tl[i] = 0;
tr[i] = K + 1;
}
for (int it = 0; it < LOG_K; it++) {
for (int i = 0; i <= K + 1; i++) {
query[i].clear();
}
for (int i = 1; i <= n; i++) {
if (tl[i] < tr[i]) {
int mid = (tl[i] + tr[i]) / 2;
query[mid].push_back(i);
}
}
for (int m = 0; m <= K; m++) {
if (m > 0) {
add_updates(m, m);
}
for (int x : query[m]) {
if (can(x, m)) {
tr[x] = m;
} else {
tl[x] = m + 1;
}
}
}
remove_updates(1, K);
}
for (int i = 1; i <= n; i++) {
if (tl[i] == K + 1) {
res[i] = -1;
} else {
res[i] = tl[i];
}
}
}