Let $T$ be the upper bound on $t_i$.
Subtask 1: $O(NT)$ per query
We can simulate the buckets for each query.
Subtask 2: $O(N)$ per query
We can observe that the buckets are periodic. Specifically, there exists a $b_i$ such that bucket $i$ is flipped at times $(i-1)+kb_i$ for all integers $k\geq 1$. We can calculate that $b_1=a_1+1$ and $b_i=\left\lceil\frac{a_i}{a_{i-1}}\right\rceil b_{i-1}$ for $i\geq 2$ since each bucket flips after some integer number of flips of the bucket above. For each query, we can calculate the periods for each bucket and then output the answer $a_N\cdot \left\lfloor\frac{\max(t-(N-1),0)}{b_N}\right\rfloor$.
Implementation note: One way to avoid integer overflow when computing $b_N$ (which can exceed $10^{18}$) is to instead maintain $\left\lfloor\frac{\max(t-(N-1),0)}{b_i}\right\rfloor$ as $i$ increases from $1$ to $N$ using division.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int cdiv(int a, int b) { return (a + b - 1) / b; }
const ll mx = 1e18;
void solve() {
int n;
cin >> n;
vector<int> a(n);
for (auto &i : a) cin >> i;
// solve
int q;
cin >> q;
while (q--) {
ll i, v, t;
cin >> i >> v >> t;
a[--i] = v;
ll ans = max(0LL, t - n + 1);
ans /= a[0] + 1;
for (int i = 1; i < n; ++i) {
ll r = cdiv(a[i], a[i - 1]);
ans /= r;
}
ans *= a.back();
cout << ans << '\n';
}
}
signed main() {
cin.tie(0)->sync_with_stdio(0);
solve();
}
Full solution: $O(\log_2 T)$ per query
When $a_i\leq a_{i-1}$, then $\left\lceil\frac{a_i}{a_{i-1}}\right\rceil=1$, so we can effectively ignore these terms. Hence, we only need to consider terms where $a_i>a_{i-1}$. For these terms, $\left\lceil\frac{a_i}{a_{i-1}}\right\rceil\geq 2$. If $b_N>10^{18}$, then the answer will always be $0$. Thus, the answer can only be nonzero if there exist at most $\log_2(10^{18})\le 60$ indexes $i$ such that $a_i>a_{i-1}$.
Store these indexes in a set. Each update adds or removes at most $2$ elements from the set. To answer a query, we manually calculate the answer using the set's indices or break if we detect that the answer is zero, which always happens within $60$ indices.
Akshaj's C++ code:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int cdiv(int a, int b) { return (a + b - 1) / b; }
const ll mx = 1e18;
void solve() {
int n;
cin >> n;
vector<int> a(n);
for (auto &i : a) cin >> i;
// precomp
set<int> s;
auto upd = [&](int i) {
if (i != 0 && a[i] > a[i - 1]) s.insert(i);
else s.erase(i);
};
for (int i = 1; i < n; i++) upd(i);
// solve
int q;
cin >> q;
while (q--) {
ll i, v, t;
cin >> i >> v >> t;
a[--i] = v;
upd(i);
if (i < n - 1) upd(i + 1);
ll ans = max(0LL, t - n + 1);
ans /= a[0] + 1;
for (int i : s) {
ll r = cdiv(a[i], a[i - 1]);
ans /= r;
if (ans == 0) break;
}
ans *= a.back();
cout << ans << '\n';
}
}
signed main() {
cin.tie(0)->sync_with_stdio(0);
solve();
}
Danny Mittal's Java code:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
import java.util.TreeSet;
public class MilkBuckets {
public static void main(String[] args) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(in.readLine());
long[] sizes = new long[n + 1];
StringTokenizer tokenizer = new StringTokenizer(in.readLine());
TreeSet<Integer> increases = new TreeSet<>();
for (int j = 1; j <= n; j++) {
sizes[j] = Long.parseLong(tokenizer.nextToken());
if (j > 1 && sizes[j] > sizes[j - 1]) {
increases.add(j);
}
}
StringBuilder out = new StringBuilder();
for (int q = Integer.parseInt(in.readLine()); q > 0; q--) {
tokenizer = new StringTokenizer(in.readLine());
int j = Integer.parseInt(tokenizer.nextToken());
sizes[j] = Long.parseLong(tokenizer.nextToken());
long t = Long.parseLong(tokenizer.nextToken());
if (j > 1 && sizes[j] > sizes[j - 1]) {
increases.add(j);
} else {
increases.remove(j);
}
if (j < n && sizes[j + 1] > sizes[j]) {
increases.add(j + 1);
} else {
increases.remove(j + 1);
}
long offset = n - 1;
long cycle = sizes[1] + 1;
boolean tooLong = false;
for (int k : increases) {
long factor = (sizes[k] + sizes[k - 1] - 1) / sizes[k - 1];
if (1_000_000_000_000_000_000L / cycle < factor) {
tooLong = true;
break;
}
cycle *= factor;
}
if (tooLong) {
out.append(0);
} else {
out.append(((t - offset) / cycle) * sizes[n]);
}
out.append('\n');
}
System.out.print(out);
}
}
Bonus: solve in $O(N+Q)$!