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;
}
posted @ 2026-02-02 21:15  tangtianyao0123  阅读(2)  评论(0)    收藏  举报