P17688 [IO 2025 #2] 失物招领处 题解

题目大意

有 \(n\) 个在一条数轴上的车站,第 \(i\) 辆车的坐标为 \(x_i\) 以及点权为 \(p_i\),且坐标递增。然后我们需要求当某个车站 \(i\) 放置失物招领处时的 \(\sum_{j\ne i}p_j\rvert x_i-x_j\rvert\) 值最小,如果有多个车站该值相等且最小,取下标更小者。

思路分析

首先,十分容易想到有一种 \(O(n^2)\) 的方法,这里不再赘述。

然后我们怎么优化呢?其实我们可以发现,当我们已经设定一个车站放置失物招领处时其他车站可以分为两种,第一种下标小于这个车站,第二种下标大于这个车站,那么我们设数组 \(dp_i\) 表示第 \(i\) 辆车的那个式子的值,那么可得转移方程:

\[dp_i=\begin{cases}dp_{i-1}+(x_i-x_{i-1})\sum_{j<i}p_j-(x_i-x_{i-1})\sum_{j>=i}p_j & i>1\\\sum_{j>1}(x_j-x_1)p_j & i=1 \end{cases} \]

也就是说,每次往后枚举时,我们会和第一种车站往后一些距离,这个距离就是他和前面的车站的距离,然后还会和第二种车站拉近一些距离,这个距离也是他和前面的车站的距离。对了,\(dp_1\) 我们 \(O(n)\) 预处理就行了,还有求和我们用前缀和提前处理就行了。时间复杂度 \(O(n)\)。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=3e5+5;
ll a[N],p[N],s[N];
int main(){
  ios::sync_with_stdio(0);
  cin.tie(0),cout.tie(0);
  int n;cin>>n;
  for(int i=1;i<=n;i++)cin>>a[i];
  for(int i=1;i<=n;i++)cin>>p[i];
  for(int i=1;i<=n;i++)s[i]=s[i-1]+p[i];//前缀和
  ll sum=0,ans=0;int m=1;//这里我就没用DP数组了,直接用变量sum来代替数组
  for(int i=2;i<=n;i++)sum+=(a[i]-a[1])*p[i];//dp[1]
  ans=sum;
  for(int i=2;i<=n;i++){
    sum+=s[i-1]*(a[i]-a[i-1]);
    sum-=(s[n]-s[i-1])*(a[i]-a[i-1]);
    if(sum<ans)ans=sum,m=i;
    //注意,是求最小的
  }
  cout<<m;
}
posted @ 2026-10-17 18:51  SCX2014  阅读(1)  评论(0)    收藏  举报