带权中位数(变化量)

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;

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值