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;
}

浙公网安备 33010602011771号