最优贸易简化版

最优贸易简化版

题目描述 C国有 nn 座城市,编号是 11 到 nn ,编号为 ii 的城市有路到编号为 i+1i+1 的城市(编号为 nn
的城市没有路到其他的城市)。
C国幅员辽阔,各地的资源分布情况各不相同,这就导致了同一种商品在不同城市的价格不一定相同。但是,同一种商品在同一个城市的买入价和卖出价始终是相同的。
商人阿龙再次来到C国旅游。他还是想贩卖水晶赚取旅费,在某个城市买入,再另一个城市卖出。
他将从编号为 aa 的城市到编号到 bb 的城市。请你帮他算算,最多能赚多少钱。
注:他最多进行一次买入和一次卖出。
输入 第一行两个整数 nn 和 mm ,表示 nn 个城市和 mm 个询问。
第二行 n个整数,表示 n 座城市水晶的买入和卖出的价格。接下来 mm 行,每行两个整数 a,b ,表示阿龙要从编号为 a的城市到编号为 b的城市(a小于b)
输出 对于每个询问输出阿龙能赚多少钱。

一.并查集

通过离线对询问进行处理,然后由1扫描至n,利用getfa函数对数据进行处理
复杂度为O(n+m)【最快】

#include<cstdio>
#include<iostream>
#include<vector>
using namespace std;
#define M 500005
#define FOR(i,x,y) for(int i=(x);i<(y);i++)
typedef pair<int,int>P;
vector<P>q[M];
int par[M],mx[M],mi[M],res[M],A[M],ans[M];
int getfa(int x){
    if(par[x]==x)return x;
    int t=par[x];
    par[x]=getfa(par[x]);
    if(res[x]<mx[t]-mi[x])res[x]=mx[t]-mi[x];
    if(res[x]<res[t])res[x]=res[t];
    if(mi[x]>mi[t])mi[x]=mi[t];
    if(mx[x]<mx[t])mx[x]=mx[t];
    return par[x];
}
inline void Rd(int &res){
    res=0;char c;
    while(c=getchar(),c<47);
    do res=(res<<3)+(res<<1)+(c&15);
    while(c=getchar(),c>=48);
}
int main(){
    int n,m;
    Rd(n),Rd(m);
    FOR(i,1,n+1)Rd(A[i]);
    FOR(i,1,m+1){//离线
        int x,y; 
        Rd(x),Rd(y);
        P t;t.first=x,t.second=i;
        q[y].push_back(t);
    }
    FOR(i,1,n+1){par[i]=i;mx[i]=mi[i]=A[i];}//初始化 
    FOR(i,1,n+1){
        FOR(j,0,q[i].size()){
            getfa(q[i][j].first);
            ans[q[i][j].second]=res[q[i][j].first];
        }
        par[i]=i+1;
    }
    FOR(i,1,m+1)printf("%d\n",ans[i]);
    return 0;
}

二.线段树

通用的状态转移方程为:

void up(node &fa,node& l,node& r){
    fa.res=max(l.res,r.res);
    fa.res=max(r.mx-l.mi);
    fa.mi=min(l.mi,r.mi);
    fa.mx=max(l.mx,r.mx);
}

然后就是裸的线段树,套个模板就好了

三.分块

其实也秉承了线段树up的思想,经过优化n^3/2也可以过
耗时

#include<cstdio>
#include<cmath>
using namespace std;
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define M 500005
int A[M];
struct node{
    int mx,mi,v;
}B[M];
void init(int n,int s){
    FOR(i,0,n/s){
        B[i].mi=1e9;
        B[i].mx=0;
        B[i].v=0;   
    }
    FOR(i,1,n){
        if(A[i]-B[i/s].mi>B[i/s].v)B[i/s].v=A[i]-B[i/s].mi;
        if(B[i/s].mi>A[i])B[i/s].mi=A[i];
        if(B[i/s].mx<A[i])B[i/s].mx=A[i];
    }
}
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    FOR(i,1,n)scanf("%d",&A[i]);
    init(n,sqrt(n));
    int s=sqrt(n);
    while(m--){
        int x,y;
        scanf("%d%d",&x,&y);
        int ka=x/s,kb=y/s,ans=0;
        if(ka==kb){
            int mi=1e9;
            FOR(i,x,y){
                if(A[i]-mi>ans)ans=A[i]-mi;
                if(A[i]<mi)mi=A[i];
            }
        }
        else{
            int mi=1e9;
            FOR(i,x,(ka+1)*s-1){
                if(A[i]-mi>ans)ans=A[i]-mi;
                if(A[i]<mi)mi=A[i];
            }
            FOR(i,ka+1,kb-1){
                if(B[i].mx-mi>ans)ans=B[i].mx-mi;
                if(B[i].v>ans)ans=B[i].v;
                if(B[i].mi<mi)mi=B[i].mi;
            }
            FOR(i,kb*s,y){
                if(A[i]-mi>ans)ans=A[i]-mi;
                if(A[i]<mi)mi=A[i];  
            }
        } 
        printf("%d\n",ans);
    }
    return 0;
}

四.倍增

实现起来与分块相似
但复杂度为nlog(n)

贴上部分代码

预处理

FOR(i,1,n){
    mx[i][0]=max(A[i],A[i+1]);
    mi[i][0]=min(A[i],A[i+1]);
    val[i][0]=max(val[i][0],A[i+1]-A[i]);
}
FOR(j,1,S-1){
    FOR(i,1,n){
        if(i+(1<<j-1)>n)continue;
        mx[i][j]=max(mx[i][j-1],mx[i+(1<<j-1)][j-1]);
        mi[i][j]=min(mi[i][j-1],mi[i+(1<<j-1)][j-1]);
        val[i][j]=max(val[i][j-1],val[i+(1<<j-1)][j-1]);
        val[i][j]=max(val[i][j],mx[i+(1<<j-1)][j-1]-mi[i][j-1]);
    }
}

solve

int len=y-x,mn=1e9,ans=0;
FOR(i,0,S-1){
    if(len&1<<i){   
        ans=max(ans,val[x][i]);
        ans=max(ans,mx[x][i]-mn);           
        mn=min(mn,mi[x][i]);
        x+=1<<i;
    }
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值