P13773题解报告
这是一个五年级菜逼的第 14 篇题解,望通过。
我们发现,如果他们的相遇点为 \(i\),那么从出发点开始的列车行驶的时间为 \(a_1 + a_2 + \cdots + a_{i-1}\),从终点站行驶的车时间为 \(a_i + a_{i + 1} + \cdots + a_{n-1}\)。看到区间和,我们可以使用前缀和处理(这里不欢迎线段树大佬),那么两段时间就变成了 \(s_{i-1}\) 和 \(s_n - s_{i - 1}\),他们的差为 \(|2s_{i-1} - s_n|\),所以我们让他最小,就是让 \(s_{i-1}\) 最接近 \(\frac{1}{2}s_n\)。而我们知道,前缀和是有单调性的,所以可以二分找到这个值,那么这道题就做完了——吗?
还有一个相对恶心的东西就是还会有修改的操作,所以普通的前缀和是无法处理的,我们需要一个神秘的东西——树状数组(说线段树的给我出去),所以二分的时候,每次查询 \(sum(mid)\) 还需要 \(\log n\) 的复杂度。
实现的小技巧:
- 如果每次修改都需要查询 \(sum(n)\) 会很慢,所以我们可以用一个变量 \(S\) 表示 \(sum(n)\),每次修改时进行维护即可。
- 这里的单点修改不是加,所以还需要统计出原来的位置上的数(就是代码中的 \(a\) 数组),将赋值变成加。
code(无注释,勿喷,有防抄袭):
#include<bits/stdc++.h>
#define int long long
#define lowbit(x) x & -x
const int N = 1e5 + 1;
int n, a[N], s, q, j, x, l, r;
struct BIT{
int a[N];
void update(int x, int k){
while(x < n){
a[x] += k;
x += lowbit(x);
}
}
int sum(int x){
int ans = 0;
while(x){
ans += a[x];
x -= lowbit(x);
}
return ans;
}
}aaaaa;
int main(){
ios::sync_with_stdio(0), cin.tie(0);
cin >> n;
for(int i = 1; i < n; i++) cin >> a[i], aaaaa.update(i, a[i]);
s = aaaaa.sum(n);
cin >> q;
l = 1, r = n;
while(l < r){
int mid = l + r >> 1;
2 * aaaaa.sum(mid) <= s ? l = mid : r = mid - 1;
}
cout << min(abs(2 * aaaaa.sum(l) - s), abs(2 * aaaaa.sum(l + 1) - s)) << '\n';
while(q--){
cin >> j >> x;
s += x - a[j];
aaaaa.update(j, x - a[j]);
a[j] = x;
l = 1, r = n;
while(l < r){
int mid = l + r >> 1;
2 * aaaaa.sum(mid) <= s ? l = mid : r = mid - 1;
}
cout << min(abs(2 * aaaaa.sum(l) - s), abs(2 * aaaaa.sum(l + 1) - s)) << '\n';
}
return 0;
}