【线段树】Traveling Merchant

题目

n n n个城市,它们在卖同一种物品。每个城市这个物品的起始价 v [ i ] v[i] v[i],星期一和星期天加个为 v [ i ] v[i] v[i],星期二和星期六价格为 v [ i ] + d [ i ] v[i]+d[i] v[i]+d[i],星期三和星期五价格为 v [ i ] + 2 ∗ d [ i ] v[i]+2*d[i] v[i]+2d[i],星期四为 v [ i ] + 3 ∗ d [ i ] v[i]+3*d[i] v[i]+3d[i]。有一个人从 s s s城按顺序走到 t t t城,在过程中进行了一次买卖,求最多能赚多少。

思路

创建七个线段树,分别是反推回去的城市 1 1 1是星期几来决定的。

i d [ i ] [ j ] [ x ] id[i][j][x] id[i][j][x] i i i为现在是星期几, j j j为当前走到了哪里, x x x为现在是正方向还是反方向, i d [ i ] [ j ] [ x ] id[i][j][x] id[i][j][x]表示当前为星期 i i i,走到 j j j的时候反推回去第一天的星期数。

代码

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<ctime>
#include<iostream>
#include<string>
#include<map>
#include<queue>
#include<stack>
#include<set>
#include<vector>
#include<iomanip>
#include<list>
#include<bitset>
#include<sstream>
#include<fstream>
#include<complex>
#include<algorithm>
#if __cplusplus >= 201103L
#include <unordered_map>
#include <unordered_set>
#endif
#define int long long
using namespace std;
const int INF = 0x3f3f3f3f;
const int mod = 1e9+7;
int v[100010],d[100010],id[8][100010][2];
struct seg{
	struct sut{
		int l,r,mid,ma,mi,res;
	}tree[400040];
#define ls x<<1
#define rs x<<1|1
	int a[100100];
	void merge(sut &ans,sut a,sut b){
		ans.ma=max(a.ma,b.ma);
		ans.mi=min(a.mi,b.mi);
		ans.res=max(a.res,b.res);
		ans.res=max(ans.res,b.ma-a.mi);
	}
	void build(int x,int l,int r){
		tree[x]={l,r,(l+r)>>1,-INF,INF,-INF};
		if(l==r){
			tree[x].ma=tree[x].mi=a[l];
			return;
		}
		int mid=tree[x].mid;
		build(ls,l,mid);
		build(rs,mid+1,r);
		merge(tree[x],tree[ls],tree[rs]);
	}
	sut query(int x,int l,int r){
		if(l<=tree[x].l&&tree[x].r<=r){
			return tree[x];
		}
		int mid=tree[x].mid;
		if(r<=mid) return query(ls,l,r);
		else if(l>mid) return query(rs,l,r);
		else{
			sut ans;
			merge(ans,query(ls,l,mid),query(rs,mid+1,r));
			return ans;
		}
		return tree[x];
	}
}rt[8][2];

int cal(int n,int v,int d){
	if(n==1||n==7) return v;
	if(n==2||n==6) return v+d;
	if(n==3||n==5) return v+2*d;
	return v+3*d;
}

signed main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	int n;
	cin>>n;
//	cout<<"?"<<endl;
	for(int i=1;i<=n;i++){
		cin>>v[i]>>d[i]; 
	}
//	cout<<"~             1"<<endl;
	for(int i=1;i<=7;i++){
		int now=i;
		for(int j=1;j<=n;j++){
			int k=cal(now,v[j],d[j]);
			id[now][j][0]=i;
			rt[i][0].a[j]=k;
			now++;
			now%=7;
			if(now==0) now=7;
		}
		rt[i][0].build(1,1,n);
	}
	reverse(v+1,v+1+n);
	reverse(d+1,d+1+n);
	for(int i=1;i<=7;i++){
	//	cout<<i<<endl;
		int now=i;
		for(int j=1;j<=n;j++){
			int k=cal(now,v[j],d[j]);
			id[now][j][1]=i;
			rt[i][1].a[j]=k;
			now++;
			now%=7;
			if(now==0) now=7;
		}
		rt[i][1].build(1,1,n);
	}
	//cout<<"!"<<endl;	
	int q;
	cin>>q;
	while(q--){
		int s,t;
		cin>>s>>t;
		int ans=0;
		if(s<t){
			int p=id[1][s][0];
			ans=max(ans,rt[p][0].query(1,s,t).res);
		}
		else{
			s=n-s+1;
			t=n-t+1;
			int p=id[1][s][1];
			ans=max(ans,rt[p][1].query(1,s,t).res);
		}
		cout<<ans<<endl;
	}
	
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值