[CSP-S 2022] 策略游戏(线段树代码)
[CSP-S 2022] 策略游戏
题意:
给定两个序列A,B。每次先从A序列规定区间内取出一个数,再从B序列规定区间内取一个数,答案为两数乘积。A希望答案尽可能大,B希望答案尽可能小。
思路:
考虑特殊性质
- 保证 \(A_1,B_1>0\)
显然对于每次询问,A取区间最大值,B取区间最小值。
线段树或ST表维护区间最值即可。
- 保证每次询问有\(l_1=r_1\),或\(l_2=r_2\)
- 若A只有非负数--------B选择优先级为:最小负数,0, 最小正数
- 当A为负数--------------B选择优先级为:最大正数, 0, 最小负数
- 如果B只有非负数---- A选择优先级为:最大正数 ,0, 最小负数
- 要是B只有负数 -------A选择优先级为:最大负数 ,0, 最小正数
此时发现如果将后方得出的选择优先级再进行分类讨论就能表示出所有的选择情况,分情况选择区间所维护的信息相乘即可。
因为0无论如何归类都不会对过程产生影响,所以把0和正数一同处理。
得出有以下几种情况:
-
A 区间全为正数
(1) B 区间全为正数,A 取最大, B 取最小
(2) B 区间有正有负,A 取最小,B 取最小
(3) B 区间全为负数,A 取最小,B 取最小
-
A 区间有正有负
(1) B 区间全为正数,A 取最大,B 取最小
(2) B 区间有正有负,\(max(A 最小正数×B 最小负数,A 最大负数× B 最大正数)\)
(3) B 区间全为负数,A 取最小,B 取最大
-
A 区间全为负数
(1) B 区间全为正数,A 取最大,B 取最大
(2) B 区间有正有负,A 取最大,B 取最大
(3) B 区间全为负数,A 取最小,B 取最大
可以发现用到的信息为各区间的:
最大正数,最大负数,最小正数,最小负数。
为了方便思路也可以增加区间最大值和区间最小值两个信息。当然在AB均无特殊情况时也可以用最大正数和最小负数来代替。
所以变成了经典的区间维护最值问题,可以分别对A序列和B序列建一棵线段树,维护以上六个区间信息。
#include<bits/stdc++.h>
#define MAXN 100086
#define ls p<<1
#define rs p<<1|1
#define node p
#define int long long
#define inf 1000000001
#define itvl1 l1,r1,1,t1//A的规定区间
#define itvl2 l2,r2,1,t2//B的规定区间
using namespace std;
int n,m,q,a[MAXN],b[MAXN];
struct T{
int l,r,maxz,minz,maxf,minf;//区间左右端点,最大最小的正数(非负数),最大最小的负数
int maxn,minn;//区间最大值,区间最小值
}t1[MAXN<<2],t2[MAXN<<2];
void updata(int p,T t[]){
t[p].maxn=max(t[ls].maxn,t[rs].maxn);
t[p].minn=min(t[ls].minn,t[rs].minn);
t[p].maxz=max(t[ls].maxz,t[rs].maxz);
t[p].minz=min(t[ls].minz,t[rs].minz);
t[p].maxf=max(t[ls].maxf,t[rs].maxf);
t[p].minf=min(t[ls].minf,t[rs].minf);
}
//l,r,maxz,minz,maxf,minf;
void build(int l,int r,int p,T t[],int a[]){
t[p]={l,r};
if(l==r){
t[p].maxn=t[p].minn=a[l];
if(a[l]>=0){
t[p].maxz=t[p].minz=a[l];
t[p].minf=inf;
t[p].maxf=-inf;
}
else{
t[p].maxf=t[p].minf=a[l];
t[p].minz=inf;
t[p].maxz=-inf;
}
return ;
}
int mid=(l+r)/2;
build(l,mid,ls,t,a);
build(mid+1,r,rs,t,a);
updata(p,t);
}
inline int askmax(int l,int r,int p,T t[]){
if(l<=t[p].l&&t[p].r<=r){
return t[p].maxn;
}
int ans=-inf;
int mid=(t[p].l+t[p].r)/2;
if(l<=mid)ans=max(ans,askmax(l,r,ls,t));
if(mid<r)ans=max(ans,askmax(l,r,rs,t));
return ans;
}
inline int askmin(int l,int r,int p,T t[]){
if(l<=t[p].l&&t[p].r<=r){
return t[p].minn;
}
int ans=inf;
int mid=(t[p].l+t[p].r)/2;
if(l<=mid)ans=min(ans,askmin(l,r,ls,t));
if(mid<r)ans=min(ans,askmin(l,r,rs,t));
return ans;
}
inline int askmaxz(int l,int r,int p,T t[]){
if(l<=t[p].l&&t[p].r<=r){
return t[p].maxz;
}
int ans=-inf;
int mid=(t[p].l+t[p].r)/2;
if(l<=mid)ans=max(ans,askmaxz(l,r,ls,t));
if(mid<r)ans=max(ans,askmaxz(l,r,rs,t));
return ans;
}
inline int askminz(int l,int r,int p,T t[]){
if(l<=t[p].l&&t[p].r<=r){
return t[p].minz;
}
int ans=inf;
int mid=(t[p].l+t[p].r)/2;
if(l<=mid)ans=min(ans,askminz(l,r,ls,t));
if(mid<r)ans=min(ans,askminz(l,r,rs,t));
return ans;
}
inline int askmaxf(int l,int r,int p,T t[]){
if(l<=t[p].l&&t[p].r<=r){
return t[p].maxf;
}
int ans=-inf;
int mid=(t[p].l+t[p].r)/2;
if(l<=mid)ans=max(ans,askmaxf(l,r,ls,t));
if(mid<r)ans=max(ans,askmaxf(l,r,rs,t));
return ans;
}
inline int askminf(int l,int r,int p,T t[]){
if(l<=t[p].l&&t[p].r<=r){
return t[p].minf;
}
int ans=inf;
int mid=(t[p].l+t[p].r)/2;
if(l<=mid)ans=min(ans,askminf(l,r,ls,t));
if(mid<r)ans=min(ans,askminf(l,r,rs,t));
return ans;
}
////////////////////////
signed main(){
int f1=0,f2=0,l1,l2,r1,r2;
cin>>n>>m>>q;
for(int i=1;i<=n;i++){
cin>>a[i];
if(a[i]<0)f1=-1;
}
for(int i=1;i<=m;i++){
cin>>b[i];
if(b[i]<0)f2=-1;
}
build(1,n,1,t1,a);
build(1,m,1,t2,b);
while(q--){
cin>>l1>>r1>>l2>>r2;
if(f1==0&&f2==0){//特殊性质1:AB序列都为非负数
cout<<askmax(itvl1)*askmin(itvl2)<<'\n';
continue;
}
int ans=-1e18+1,ans2=1e9+1,ans3=1e9+1,ans4=1e9+1;
int a1=askmaxz(itvl1);//正 max
int a2=askminz(itvl1);//正 min
int a3=askmaxf(itvl1);//负 max
int a4=askminf(itvl1);//负 min
int b1=askmax(itvl2);//max
int b2=askmin(itvl2);//min
if(a1!=-inf) ans=max(ans,a1*b2);
else ans=max(ans,a3*b1);
if(a4!=inf) ans=max(ans,a4*b1);
else ans=max(ans,a2*b2);
if(a3!=-inf) ans=max(ans,a3*b1);
if(a2!=inf) ans=max(ans,a2*b2);
cout<<ans<<'\n';
}
return 0;
}