最优贸易简化版
题目描述 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;
}
}