P2672 [NOIP2015 普及组] 推销员

贪心模拟

传送门

先从这个样例开始讲解吧

6
1 2 3 4 5 6
4 2 3 5 3 1

若当前 X = 1 X = 1 X=1,那么我们可以建一个 F F F数组
F [ i ] F[i] F[i] 表示从入口进去再出来,新添加第 a [ i ] a[i] a[i] 家用户推销,不走多余路,会对答案的贡献
a n s ans ans 表示当前答案
n o w now now 表示走的最远的点

那么 X = 1 X = 1 X=1 时: F [ i ] = s [ i ] ∗ 2 + a [ i ] F[i] = s[i]*2+a[i] F[i]=s[i]2+a[i]
F = F = F= { 6 , 6 , 9 , 13 , 13 , 13 6, 6, 9, 13, 13, 13 6,6,9,13,13,13}
所以我们从 F [ i ] F[i] F[i] 中挑选一个最大值就好了
n o w = m a x . i d now = max.id now=max.id n o w = 4 now = 4 now=4 (其实5或6也可以)
a n s ans ans += F [ 4 ] F[4] F[4]
( a n s ans ans == 13 13 13)
那么想一想,这将对其他 F [ i ] F[i] F[i] 造成什么影响?
在考虑 X = 2 X = 2 X=2 时.

s [ i ] s[i] s[i] < s [ n o w ] s[now] s[now]也就是在 s [ n o w ] s[now] s[now]的左侧

F [ i ] F[i] F[i] = a [ i ] a[i] a[i]

因为可一在去 n o w now now 点的路上推销
补充: 其实先去推销 i i i 点和先去推销 n o w now now 点的疲惫值是一样的
F F F 变为{ 4 , 2 , 3 , / , 13 , 13 4, 2, 3, /, 13, 13 4,2,3,/,13,13}

s [ i ] s[i] s[i] > s [ n o w ] s[now] s[now]也就是在 s [ n o w ] s[now] s[now]的右侧

F [ i ] = ( s [ i ] − s [ n o w ] ) ∗ 2 + a [ i ] F[i] = (s[i]-s[now])*2+a[i] F[i]=(s[i]s[now])2+a[i]

因为可以在原本的路程上往前多走二段路程( s [ i ] − s [ n o w ] s[i]-s[now] s[i]s[now]) ∗ 2 *2 2 (一去一回)
所以 F F F变为{ 4 , 2 , 3 , / , 5 , 5 4, 2, 3, /, 5, 5 4,2,3,/,5,5}

现在,我们再次从 F F F数组中找出最大值的ID
n o w = 6 now = 6 now=6 ( 5 5 5 也可以 )
a n s ans ans += F[6]
( a n s ans ans == 18 18 18)
这时
F F F = { 4 , 2 , 3 , / , 3 , / 4, 2, 3, /, 3, / 4,2,3,/,3,/}
F [ 5 ] F[5] F[5] 变成了 3 3 3 ,因为它在 6 6 6 左边
右侧没有点了,所以不更新 F F F

X = 3 X = 3 X=3
a n s ans ans += F[1]
( a n s ans ans == 22 22 22)
F F F = { / , 2 , 3 , / , 3 , / /, 2, 3, /, 3, / /,2,3,/,3,/}

X = 4 X = 4 X=4
a n s ans ans += F[3]
( a n s ans ans == 25 25 25)
F F F = { / , 2 , / , / , 3 , / /, 2, /, /, 3, / /,2,/,/,3,/}

X = 5 X = 5 X=5
a n s ans ans += F[5]
( a n s ans ans == 28 28 28)
F F F = { / , 2 , / , / , / , / /, 2, /, /, /, / /,2,/,/,/,/}

X = 6 X = 6 X=6
a n s ans ans += F[2]
( a n s ans ans == 30 30 30)
F F F = { / , / , / , / , / , / /, /, /, /, /, / /,/,/,/,/,/}
最终答案为:

13
18
22
25
28
30

那么问题来了,如何快速找出最大值呢
我们使用 m a x n [ n o w ] maxn[now] maxn[now] 表示 n o w now now ~ n n n F [ i ] F[i] F[i] 的最大值 ( n o w < i < = n now < i <= n now<i<=n)
m a x n [ i ] maxn[i] maxn[i] 的作用是维护 n o w now now 右侧的 F [ i ] F[i] F[i] 的最大值
那么 n o w now now 左侧的最大 a [ i ] a[i] a[i] 之怎么维护捏?
因为 n o w now now 左侧的值需要有删除最大值的操作
那么不能使用类似 m a x n maxn maxn 的预处理操作
但 priority_queue 是一个不错的数据结构
可以用优先队列 q q q 维护左侧最大 a [ i ] a[i] a[i] 的值

会发现,这些操作都不会用到 F F F 数组,所以不需要建立此数组,更不需要更改它,
n o w now now 左侧的 a [ i ] a[i] a[i] 都不会改变了,而右侧的 m a x n [ n o w ] maxn[now] maxn[now] 只需要减掉 s [ n o w ] ∗ 2 s[now]*2 s[now]2 即可
m a x n [ n o w ] maxn[now] maxn[now] = s [ i ] ∗ 2 + a [ i ] s[i]*2+a[i] s[i]2+a[i] , ( i$ 是此时的最大值)
所以 m a x n [ n o w ] − s [ n o w ] ∗ 2 maxn[now]-s[now]*2 maxn[now]s[now]2 = a [ i ] + s [ i ] ∗ 2 − s [ n o w ] ∗ 2 a[i]+s[i]*2-s[now]*2 a[i]+s[i]2s[now]2 = a [ i ] + ( s [ i ] − s [ n o w ] ) ∗ 2 a[i]+(s[i]-s[now])*2 a[i]+(s[i]s[now])2 也就是上述 F [ i ] F[i] F[i] 的值

Ac Code:

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10,INF=1e9,mod=INF+7;
int n,now,cnt;
long long ans;
int a[N],s[N];
pair<int,int> maxn[N];
priority_queue<int> q;

int main()
{
	scanf("%d",&n);
	for(int i = 1;i <= n;i++)
		scanf("%d",s+i);
	for(int i = 1;i <= n;i++)
		scanf("%d",a+i);
	maxn[n+1].second = -INF;
	for(int i = n;i >= 1;i--){
		maxn[i] = maxn[i+1];
		if(s[i]*2+a[i] >= maxn[i].second)
			maxn[i] = make_pair(i,s[i]*2+a[i]);
	}
	for(int i = 1;i <= n;i++){
		int l = (q.empty() ? -INF : q.top());
		int r = (now == n ? -INF : maxn[now+1].second);
		if(l >= r-s[now]*2){
			ans += l;
			q.pop();
			printf("%lld\n",ans);
		}else {
			ans += r-s[now]*2;
			for(int i = now+1;i <= maxn[now+1].first-1;i++)
				q.push(a[i]);
			now = maxn[now+1].first;
			printf("%lld\n",ans);
		}
	}
	return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值