[NOIp2015]推销员

题目

传送门 to luogu

问题描述
阿明是一名推销员,他奉命到螺丝街推销他们公司的产品。

螺丝街是一条死胡同,出口与入口是同一个,街道的一侧是围墙,另一侧是住房。螺丝街一共有 N N N 家住房,第 i i i 家住户到入口的距离为 S i S_i Si 米。由于同一栋房子里可以有多家住户,所以可能有多家住房与入口的距离相等。

阿明会从入口进入,依次向螺丝街的 X X X 家住房推销产品,然后再原路走出去。阿明每走 1 1 1 米就会积累 1 1 1 点疲劳值,向第 i i i 家住房推销产品会积累 A i A_i Ai 点疲劳值。阿明是工作狂,他想知道,对于不同的 X X X ,在不走多余路的前提下,他最多可以积累多少点疲劳值。

输入格式
第一行有一个正整数 N N N ,表示螺丝街住房的数量。

接下来的一行有 N N N 个正整数,其中第 i i i 个整数 S i S_i Si 表示第 i i i 家住户到入口的距离。数据
保证 S 1 ≤ S 2 ≤ … ≤ S n ≤ 1 0 8 S_1\le S_2≤…≤S_n≤10^8 S1S2Sn108

接下来的一行有 N N N 个整数,其中第 i i i 个整数 A i A_i Ai 表示向第 i i i 户住户推销产品会积累的疲
劳值。数据保证 A i < 1 0 3 Ai<10^3 Ai<103

输出格式
输出 N N N 行,每行一个正整数,第 i i i 行整数表示当 X = i X=i X=i 时,阿明最多积累的疲劳值。

数据范围与提示
对于 20 % 20\% 20%的数据, 1 ≤ N ≤ 20 1≤N≤20 1N20
对于 40 % 40\% 40%的数据, 1 ≤ N ≤ 100 1≤N≤100 1N100
对于 60 % 60\% 60%的数据, 1 ≤ N ≤ 1000 1≤N≤1000 1N1000
对于 100 % 100\% 100%的数据, 1 ≤ N ≤ 100000 1≤N≤100000 1N100000

思路

说明:为了行文方便,以下称 “一户人家” 为 “点” ,将要推销的点的集合为 X X X

因为不走回头路,所以行走的疲劳值只有 2 max ⁡ x ∈ X S x 2\max_{x\in X} S_x 2maxxXSx ,而推销疲劳值为 ∑ x ∈ X A x \sum_{x\in X}A_x xXAx ,所以有:
a n s = 2 max ⁡ x ∈ X S x + ∑ x ∈ X A x ans=2\max_{x\in X}S_x+\sum_{x\in X}A_x ans=2xXmaxSx+xXAx

注意到 max ⁡ x ∈ X S x \max_{x\in X}S_x maxxXSx 的变化是非常难的,所以,我们不妨假设一组解。其深层原因是,我们要最大化 a n s ans ans恰好它是由 max ⁡ S x \max S_x maxSx 决定的,二者统一

在这组解中,我们将 S S S 最大的点固定,剩下的点就可以任选,目的是让 ∑ A \sum A A 最大。

很显然,剩下的点应当尽量去选择 A A A 比较大的,因为 S S S 已经不归它们管了。

所以,我们最多只需要一个点去得到较大的 S S S ,于是就至少会选前 i − 1 i-1 i1 大的 A A A

固定了 i − 1 i-1 i1 个极大的 A A A 之后,剩下的一个就应该综合考虑 S + A S+A S+A 了。这可以用 O ( n ) \mathcal O(n) O(n) 的预处理得到。

当然,也有可能不考虑 S + A S+A S+A ,直接全部选最大的 A A A S S S 就听天由命了。还是可以预处理。

上面这种情况的本质是, S + A S+A S+A 中的 S S S 却没有已经固定的大 A A A 厉害。毕竟 S + A S+A S+A 的考虑是比较狭隘的——它只考虑不属于前 i i i 大的 A A A 的点。

代码

#include <cstdio>
#include <algorithm>
#include <iostream>
using namespace std;

struct Home{
    int s,a;
    bool operator<(const Home &x)const{
        return a>x.a;
    }
};

const int MAXN=100000;
int suma[MAXN+5],s_and_a[MAXN+5],maxs[MAXN+5];
Home home[MAXN+5];

int main()
{
    int n;
    scanf("%d",&n);

    for(int i=1;i<=n;++i)
    	scanf("%d",&home[i].s);
    for(int i=1;i<=n;++i)
    	scanf("%d",&home[i].a);
    sort(home+1,home+1+n);

    for(int i=1;i<=n;++i)
    {
    	maxs[i]=max(maxs[i-1],home[i].s);
    	suma[i]=suma[i-1]+home[i].a;
    }
    for(int i=n;i>=1;--i)
    	s_and_a[i]=max(s_and_a[i+1],home[i].s*2+home[i].a);

    for(int i=1;i<=n;++i)
    	printf("%d\n",max(maxs[i]*2+suma[i],s_and_a[i]+suma[i-1]));
    return 0;
}

后记

我的所有博客中(我还是写了不少的),阅读比较多的竟然是这几篇:

  • 巴拉巴拉(记不清名字了),是一道有点意思的黑题。
  • 这篇博客,是一道绿题……
  • 普林姆正确性的证明,而最小生成树的板题是黄色的……

我无话可说。您开心就好。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值