code vs 5126 推销员 NOIP2015 (线段树+贪心)

5126 推销员 NOIP2015

 时间限制: 1 s
 空间限制: 128000 KB
 题目等级 : 黄金 Gold
题解
 查看运行结果
题目描述 Description

阿明是一名推销员,他奉命到螺丝街推销他们公司的产品。螺丝街是一条死胡同,出口与入口是同一个,街道的一侧是围墙,另一侧是住户。螺丝街一共有N家住户,第i家住户到入口的距离为Si米。由于同一栋房子里可以有多家住户,所以可能有多家住户与入口的距离相等。阿明会从入口进入,依次向螺丝街的X家住户推销产品,然后再原路走出去。阿明每走1米就会积累1点疲劳值,向第i家住户推销产品会积累Ai点疲劳值。阿明是工作狂,他想知道,对于不同的X,在不走多余的路的前提下,他最多可以积累多少点疲劳值。

输入描述 Input Description

第一行有一个正整数N,表示螺丝街住户的数量。
接下来的一行有N个正整数,其中第i个整数Si表示第i家住户到入口的距离。数据保证S1≤S2≤…≤Sn<10^8。
接下来的一行有N个正整数,其中第i个整数Ai表示向第i户住户推销产品会积累的疲劳值。数据保证Ai<10^3。

输出描述 Output Description

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

样例输入 Sample Input

【样例1】
5
1 2 3 4 5
1 2 3 4 5

【样例2】
5
1 2 2 4 5
5 4 3 4 1

样例输出 Sample Output

【样例1】
15
19
22
24
25

【样例2】
12
17
21
24
27

数据范围及提示 Data Size & Hint

1≤N≤100000
注:请用 scanf 输入。


题解:线段树+贪心

维护两颗线段树,一棵维护区间单点权值的最大值及位置,另一棵维护s[i]*2+v[i]的最大值及位置。

x=1的时候答案肯定是第二棵线段树整个区间的最大值。

考虑x=i,答案一定是单调不降的。我们每次实际上是在x=i-1的基础上加入了一个点。之前入选的点一定会出现在当前的答案中。为什么呢?假设我们没有选择后面的点作为最远的点,那么说明前面存在一个点的疲劳值很大,足以大过后面的点比前面的点多出的距离。那么这样的点是一定后继续出现在后面的答案中的。我们一定求出了i-1的答案,有两种选择,一种是从当前点的前面选取没有选过的v[i]最大的点,最后入选的点不变;另一种是从当前最靠后的点的后面选取一点,相当于最靠后的点后移。计算出两种情况的答案,取较大的即可。注意选取过的点不能重复入选。

#include
#include
#include
#include
#include
#define N 400003
using namespace std;
int n,m;
int v[N],s[N],tr[N],tr1[N],pos[N],pos1[N],ans;
struct data
{
	int x,y,pos,pos1;
};
void update(int now)
{
	if (tr[now<<1]>tr[now<<1|1]) tr[now]=tr[now<<1],pos[now]=pos[now<<1];
	else tr[now]=tr[now<<1|1],pos[now]=pos[now<<1|1];
	if (tr1[now<<1]>tr1[now<<1|1]) tr1[now]=tr1[now<<1],pos1[now]=pos1[now<<1];
	else tr1[now]=tr1[now<<1|1],pos1[now]=pos1[now<<1|1];
}
void build(int now,int l,int r)
{
	if (l==r) {
		tr[now]=v[l]; pos[now]=l;
		tr1[now]=v[l]+s[l]*2; pos1[now]=r;
		return;
	}
	int mid=(l+r)/2;
	build(now<<1,l,mid);
	build(now<<1|1,mid+1,r);
	update(now);
}
void change(int now,int l,int r,int x)
{
	if (l==r) {
		tr[now]=0; tr1[now]=0;
		return ;
	}
	int mid=(l+r)/2;
	if (x<=mid) change(now<<1,l,mid,x);
	else change(now<<1|1,mid+1,r,x);
	update(now);
}
void cover(data &a,data b)
{
	if (b.x>a.x) a.x=b.x,a.pos=b.pos;
	if (b.y>a.y) a.y=b.y,a.pos1=b.pos1;
}
data qjask(int now,int l,int r,int ll,int rr)
{
	if (ll>rr) {
		data a; a.x=0; a.y=0;
		return a;
	}
	if (ll<=l&&r<=rr) {
		data a; a.x=tr[now]; a.y=tr1[now];
		a.pos=pos[now]; a.pos1=pos1[now];
		return a;
	}
	int mid=(l+r)/2;
	data a; a.x=0; a.y=0;
	if (ll<=mid) cover(a,qjask(now<<1,l,mid,ll,rr));
	if (rr>mid) cover(a,qjask(now<<1|1,mid+1,r,ll,rr));
	return a;
}
int main()
{
	//freopen("a.in","r",stdin);
	scanf("%d",&n);
	for (int i=1;i<=n;i++)  scanf("%d",&s[i]);
	for (int i=1;i<=n;i++)  scanf("%d",&v[i]);
	build(1,1,n);
	printf("%d\n",tr1[1]); int k=pos1[1]; 
	int ans=tr1[1]; 
	data a; a=qjask(1,1,n,k,k); 
	int sum=a.x;
	change(1,1,n,pos1[1]);
	for (int i=2;i<=n;i++){
		data a=qjask(1,1,n,1,k-1);
		data b=qjask(1,1,n,k+1,n);
		int t=a.x+ans;
		int t1=sum+b.y;
		if (t>t1) {
			change(1,1,n,a.pos);
			ans=t; sum+=a.x;
		}
		else {
		    k=b.pos1;
			change(1,1,n,b.pos1);
			ans=t1; sum+=b.x;
		}
		printf("%d\n",ans);
	}
}





你可能感兴趣的:(贪心,线段树)