https://ac.nowcoder.com/acm/contest/20960/1013
结论:按位置排好序后对人数做前缀和,人数恰好超过总人数一半的位置就是距离最小的点。
证明:假设不是,向左移动一格,左边的人少移动一格,右边的人多移动一格,但左边的人的数量小于人数的一半,右边的人数量多于人数的一半,显然不是更优解;同理,向右移动一格,右边的人少移动一格,左边的人多移动一格,此时左边的人多于人数的一半,右边的人小于人数的一半,也不是更优解,因此结论得证。
#include<bits/stdc++.h>
#define endl '\n'
#define pii pair<int,int>
using namespace std;
const int maxn = 2e5+3;
struct person
{
int loc,number,cnt;
};
bool cmp(person x,person y)
{
return x.loc<y.loc;
}
person p[maxn];
void solve()
{
int n; cin>>n;
for(int i=1;i<=n;i++)
cin>>p[i].loc;
for(int i=1;i<=n;i++)
cin>>p[i].number;
int sum=0;
for(int i=1;i<=n;i++)
sum+=p[i].number,p[i].cnt=i;
sort(p+1,p+1+n,cmp);
int temp=0;
for(int i=1;i<=n;i++)
{
temp+=p[i].number;
if(temp>=sum/2)
{
cout<<p[i].cnt<<endl;
return ;
}
}
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T; T=1;
while(T--) solve();
return 0;
}
非结论做法
先假设第一个点是集合点,算出对应的距离,记作
d
i
s
[
1
]
dis[1]
dis[1],那么
d
i
s
[
2
]
=
d
i
s
[
1
]
+
前面的人
∗
多走的
−
后面的人
∗
少走的
dis[2]=dis[1]+前面的人*多走的-后面的人*少走的
dis[2]=dis[1]+前面的人∗多走的−后面的人∗少走的,把每个点作为集合点对应的距离都找出来找出最小的即可,这样需要维护一个人数的前缀和数组。
#include<bits/stdc++.h>
#define endl '\n'
#define pii pair<int,int>
#define int long long
using namespace std;
const int maxn = 2e5+3;
struct person
{
int loc,number,cnt;
};
bool cmp(person x,person y)
{
return x.loc<y.loc;
}
person p[maxn];
int prefix[maxn],dis[maxn];
void solve()
{
int n; cin>>n;
for(int i=1;i<=n;i++)
cin>>p[i].loc;
for(int i=1;i<=n;i++)
cin>>p[i].number;
int sum=0;
sort(p+1,p+1+n,cmp);
// 维护人数的前缀和
for(int i=1;i<=n;i++)
{
prefix[i]=prefix[i-1]+p[i].number;
}
int min_s=0;
int s=0;
// 先假设1号点为集合点,计算距离
for(int i=2;i<=n;i++)
{
min_s+=p[i].number*(p[i].loc-p[1].loc);
}
dis[1]=min_s;
// 依次将其他节点作为集合点,利用变化量求解新的距离,得到最优答案
int ans=1;
for(int i=2;i<=n;i++)
{
dis[i]=2*prefix[i-1]+dis[i-1]-prefix[n];
if(min_s>dis[i])
{
min_s=dis[i];
ans=i;
}
}
cout<<ans<<endl;
}
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T; T=1;
while(T--) solve();
return 0;
}