E - 会社経営シミュレーション / Company Management Simulation Editorial by admin
Claude 4.6 Opus (Thinking)Overview
In a business plan that repeats an \(N\)-day cycle infinitely, for each query we need to efficiently determine “starting from day \(L\) with initial funds \(S\), on which day does bankruptcy first occur?”
Analysis
Key Insight
Define the daily fund change as \(D_i = A_i - B_i - C_i\), and the prefix sum \(P[i] = \sum_{k=1}^{i} D_k\) (with \(P[0] = 0\)).
When starting business from day \(L\) with initial funds \(S\), the funds at the end of the \(k\)-th day counting from day \(L\) (plan day \(d\)) are: $\(S + P[d] - P[L-1]\)$ (within the same cycle)
The bankruptcy condition is \(S + P[d] - P[L-1] < 0\), that is: $\(P[d] < P[L-1] - S\)$
Handling Cycles
After the first incomplete cycle (day \(L\) through day \(N\)), during day \(k\) of the \((m+1)\)-th complete cycle (after \(m\) complete cycles), the funds are: $\(S + (P[N] - P[L-1]) + m \cdot T + P[k] = S - P[L-1] + (m+1) \cdot T + P[k]\)$
Here \(T = P[N]\) (net profit per cycle). The bankruptcy condition is: $\(P[k] < P[L-1] - S - (m+1) \cdot T\)$
Case Analysis
- When \(T \geq 0\): As cycles accumulate, the threshold decreases, so we only need to check the first incomplete cycle and the first complete cycle. If bankruptcy doesn’t occur in either, it never will.
- When \(T < 0\): As cycles accumulate, the threshold increases, and bankruptcy will inevitably occur. We calculate which cycle bankruptcy first occurs in.
Algorithm
Preprocessing: Compute the prefix sum \(P[i]\) and store it in a segment tree. The segment tree manages range minimums and can find “the leftmost position with a value less than a given threshold” in \(O(\log N)\).
Processing each query:
- First incomplete cycle (\([L, N]\)): Search for the leftmost \(k\) where \(P[k] < P[L-1] - S\). If found, the answer is \(k - L + 1\).
- When \(T \geq 0\): Search \([1, N]\) with threshold \(P[L-1] - S - T\). If found, the answer is \((N - L + 1) + k\); if not found, the answer is \(0\) (no bankruptcy).
- When \(T < 0\): Using the overall minimum \(\min P\), compute the first cycle number \(m\) where bankruptcy occurs in \(O(1)\): $\(m = \left\lfloor \frac{-(P[L-1] - S - \min P)}{-T} \right\rfloor\)\( Within that cycle, find the leftmost position \)k\( using threshold \)P[L-1] - S - (m+1) \cdot T\(, and the answer is \)(N - L + 1) + m \cdot N + k$.
Complexity
- Time complexity: \(O(N + Q \log N)\)
- \(O(N)\) for segment tree construction, \(O(\log N)\) per query for segment tree searches
- Space complexity: \(O(N)\)
- Segment tree and prefix sum array
Implementation Notes
Use
long longsince funds and prefix sums can become very large (\(S\) can be up to \(10^{12}\), and prefix sums can also grow large).Design the segment tree to return \(-1\) when no position satisfying “leftmost position below threshold” exists.
When \(T < 0\), perform integer division correctly when computing \(m\) (if \(R = P[L-1] - S - \min P\) is non-negative, then \(m = 0\)).
Align the segment tree size to a power of 2, and initialize unused parts with
LLONG_MAX(a sufficiently large value) to prevent out-of-range effects.Source Code
#include <bits/stdc++.h>
using namespace std;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int N, Q;
cin >> N >> Q;
vector<long long> P(N + 1, 0);
for (int i = 1; i <= N; i++) {
long long a, b, c;
cin >> a >> b >> c;
P[i] = P[i-1] + (a - b - c);
}
long long T = P[N];
int sz = 1;
while (sz < N) sz *= 2;
vector<long long> tree(2 * sz, LLONG_MAX);
for (int i = 1; i <= N; i++) {
tree[sz + i - 1] = P[i];
}
for (int i = sz - 1; i >= 1; i--) {
tree[i] = min(tree[2*i], tree[2*i+1]);
}
long long minP = tree[1];
function<int(int, int, int, int, int, long long)> find_left =
[&](int node, int nl, int nr, int ql, int qr, long long thresh) -> int {
if (nl > qr || nr < ql) return -1;
if (tree[node] >= thresh) return -1;
if (nl == nr) return nl;
int mid = (nl + nr) / 2;
int res = find_left(2*node, nl, mid, ql, qr, thresh);
if (res != -1) return res;
return find_left(2*node+1, mid+1, nr, ql, qr, thresh);
};
auto query = [&](int l, int r, long long threshold) -> int {
return find_left(1, 1, sz, l, r, threshold);
};
for (int q = 0; q < Q; q++) {
int L;
long long S;
cin >> L >> S;
long long thresh1 = P[L-1] - S;
int pos = query(L, N, thresh1);
if (pos != -1) {
cout << (pos - L + 1) << "\n";
continue;
}
if (T >= 0) {
long long thresh = P[L-1] - S - T;
int pos2 = query(1, N, thresh);
if (pos2 != -1) {
cout << (long long)(N - L + 1) + pos2 << "\n";
} else {
cout << 0 << "\n";
}
} else {
long long R = P[L-1] - S - minP;
long long m;
if (R >= 0) {
m = 0;
} else {
m = (-R) / (-T);
}
long long thresh = P[L-1] - S - (m + 1) * T;
int pos2 = query(1, N, thresh);
long long answer = (long long)(N - L + 1) + (long long)m * N + pos2;
cout << answer << "\n";
}
}
return 0;
}
This editorial was generated by claude4.6opus-thinking.
posted:
last update: