P8818 [CSP-S 2022] 策略游戏 题解
P8818 [CSP-S 2022] 策略游戏 题解
题目链接
简化题意
小
小
简要思路
分情况讨论:
-
当
时,小 一定会选择一个最小的 ; -
当
时,小 一定会选择一个最大的 。
再来考虑小
-
当
时,因为小 会选择最小的 ,所以:-
当
时,小 会让 更大,因为正数 正数 正数; -
当
时,小 会让 更小,即选择最小的非负数(因为现在讨论的是 的情况),因为负数 正数 负数。
-
-
当
时,因为小 会选择最大的 ,所以:-
当
时,小 会让 更大,即选择最大的负数(同理,因为现在讨论的是 的情况),因为正数 负数 负数; -
当
时,小 会让 更小,因为负数 负数 正数。
-
综上所述,小
-
选择区间内最大的数;
-
选择区间内最小的非负数;
-
选择区间内最大的负数;
-
选择区间内最小的数。
因此我们只需要分别讨论得到小
而因为我们也要考虑小
-
区间内的最大值; -
区间内的最小值; -
区间内的最小非负数(开始时将其赋值为一个极大值(因为维护时取最小值),从而方便判断该区间内是否有非负数); -
区间内的最大负数(同理,开始时赋为一个极小值(因为维护时取最大值),从而方便判断该区间内是否有负数); -
区间内的最大值; -
区间内的最小值。
剩下部分就是 ST 表的板子了,这里放个 ST 表的讲解:
完整代码
#include<bits/stdc++.h> #define int long long #define endl '\n' const int MAXN=1e5+5; const int MAX_log=25; const int MAX=LONG_LONG_MAX;//极值 int n,m,q; int l1,r1,l2,r2; int lg[MAXN];//log(i)=lg[i] 即 2^(lg[i])<=i int a[MAXN],b[MAXN]; //6 个 ST 表(第一维起点,第二维长度[2^j]) int a_max[MAXN][MAX_log];//a 区间最大值 int a_min[MAXN][MAX_log];//a 区间最小值 int b_max[MAXN][MAX_log];//b 区间最大值 int b_min[MAXN][MAX_log];//b 区间最小值 int af_max[MAXN][MAX_log];//a 负数区间最大值 int az_min[MAXN][MAX_log];//a 非负数区间最小值 signed main(){ std::cin>>n>>m>>q; //---------ST 表的预处理--------- for(int i=1;i<=n;i++){ std::cin>>a[i]; a_max[i][0]=a_min[i][0]=a[i]; if(a[i]<0){ af_max[i][0]=a[i]; az_min[i][0]=MAX;//暂时没有非负数 }else{ af_max[i][0]=-MAX;//暂时没有负数 az_min[i][0]=a[i]; } } for(int i=1;i<=m;i++){ std::cin>>b[i]; b_max[i][0]=b_min[i][0]=b[i]; } //---------lg 数组的预处理--------- for(int i=2;i<=std::max(n,m)+2;i++) lg[i]=lg[i/2]+1; //---------倍增思想(分开)的维护 for(int j=1;j<=lg[n];j++)//枚举区间长度 for(int i=1;i+(1<<j)-1<=n;i++){//枚举区间起点(注意先长度再起点) int now=i+(1<<(j-1));//第一个区间终点-1、第二个区间起点 a_max[i][j]=std::max(a_max[i][j-1],a_max[now][j-1]); af_max[i][j]=std::max(af_max[i][j-1],af_max[now][j-1]); a_min[i][j]=std::min(a_min[i][j-1],a_min[now][j-1]); az_min[i][j]=std::min(az_min[i][j-1],az_min[now][j-1]); //以 i 为起点 长度为 2^j //以 i 为起点 长度为 2^(j-1) //以 i+2^(j-1) 为起点 长度为 2^(j-1) } for(int j=1;j<=lg[m];j++)//区间长度 for(int i=1;i+(1<<j)-1<=m;i++){//区间起点 int now=i+(1<<(j-1)); b_max[i][j]=std::max(b_max[i][j-1],b_max[now][j-1]); b_min[i][j]=std::min(b_min[i][j-1],b_min[now][j-1]);//同上 } //---------ST 表的查询--------- //两个 2的幂 覆盖整个序列 a a ab ab b b while(q--){ std::cin>>l1>>r1>>l2>>r2; int alg=lg[r1-l1+1],blg=lg[r2-l2+1];//a,b 序列的长度 2^alg 2^blg int sta=r1-(1<<alg)+1,stb=r2-(1<<blg)+1;//后一个区间起点=终点-区间长度+1 int A_max=std::max(a_max[l1][alg],a_max[sta][alg]); int Af_max=std::max(af_max[l1][alg],af_max[sta][alg]); int A_min=std::min(a_min[l1][alg],a_min[sta][alg]); int Az_min=std::min(az_min[l1][alg],az_min[sta][alg]); //前一个区间(以 l1 开头 长度为 alg) //后一个区间(以 sta 开头,长度为 alg) int B_max=std::max(b_max[l2][blg],b_max[stb][blg]); int B_min=std::min(b_min[l2][blg],b_min[stb][blg]); //前一个区间(以 l2 开头 长度为 blg) //后一个区间(以 stb 开头,长度为 blg) int maxn=-MAX; //分类讨论正负性,考虑小 B 的最优选择 maxn=std::max(maxn,A_max*(A_max>=0?B_min:B_max)); maxn=std::max(maxn,A_min*(A_min>=0?B_min:B_max)); if(Af_max!=-MAX)maxn=std::max(maxn,Af_max*(Af_max>=0?B_min:B_max));//有负数才更新 if(Az_min!=MAX)maxn=std::max(maxn,Az_min*(Az_min>=0?B_min:B_max));//有非负数才更新 std::cout<<maxn<<endl; } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】