P8818 [CSP-S 2022] 策略游戏 题解
思路
题意:求一个特定矩形中每一行的最小值的最大值。
考虑分类讨论。
注意,由于 \(0\) 也需要考虑,所以下文中的正数和负数都包括了 \(0\)。
-
\(\text{a}\) 全部都是正的。
由于 \(\text{a}\) 全部都是正的。
那么无论小L选择什么,小Q总是会选择 \(\text{b}\) 序列中最小值。
所以小L只需要从最大值(小Q选的是正数)和最小值(小Q选的是负数)中选一个就可以了。
-
\(\text{a}\) 全部都是负的。
与第一点类似。
由于 \(\text{a}\) 全部都是负的。
那么无论小L选择什么,小Q总是会选择 \(\text{b}\) 序列中最大值。
所以小L只需要从最大值(小Q选的是负数)和最小值(小Q选的是正数)中选一个就可以了。
-
\(\text{b}\) 全部都是正的
由于我们已经考虑了上面两点,所以现在 \(\text{a}\) 序列必然是既有正数又有负数。
那么小L一定会选择最大值,小Q也一定会选择最小值。
-
\(\text{b}\) 全部都是负的
和上面类似,其实就是反过来
那么小L一定会选择最小值,小Q也一定会选择最大值。
-
没有特殊限制
考虑到两个序列都是既有正数又有负数。
那么就可以发现如果小L选择的是一个负数,那么小Q就会选择最大值;如果小L选择的是一个正数,那么小Q就会选择最小值。
所以小L一定会选择最接近零的正数和负数,也就是正数最小值和负数最大值。
考虑一下上面需要求的东西。
\(\text{a}\) 序列最大最小值,\(\text{b}\) 序列最大最小值,\(\text{a}\) 序列正数最小值和 \(\text{a}\) 序列负数最大值。
这个开四颗线段树就可以了。
时间复杂度 \(O(n\log n)\)。
代码也比较好写。
Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 100010;
int n , m , q , a[N] , b[N];
int s[N] , sum1[N] , sum2[N] , sum3[N] , sum4[N];
struct Node
{
int l , r , minn , maxx;
};
struct Tree
{
Node t[N * 4];
inline void push_up(int p)
{
t[p].minn = min(t[p * 2].minn , t[p * 2 + 1].minn);
t[p].maxx = max(t[p * 2].maxx , t[p * 2 + 1].maxx);
}
inline void build(int p , int l , int r)
{
t[p].l = l , t[p].r = r;
if(l == r) return t[p].minn = t[p].maxx = s[l] , void();
int mid = (l + r) / 2;
build(p * 2 , l , mid);
build(p * 2 + 1 , mid + 1 , r);
push_up(p);
}
inline int ask1(int p , int l , int r)
{
if(l <= t[p].l && t[p].r <= r)
return t[p].minn;
int mid = (t[p].l + t[p].r) / 2 , ans = 1e9;
if(l <= mid) ans = min(ans , ask1(p * 2 , l , r));
if(r > mid) ans = min(ans , ask1(p * 2 + 1 , l , r));
return ans;
}
inline int ask2(int p , int l , int r)
{
if(l <= t[p].l && t[p].r <= r)
return t[p].maxx;
int mid = (t[p].l + t[p].r) / 2 , ans = -1e9;
if(l <= mid) ans = max(ans , ask2(p * 2 , l , r));
if(r > mid) ans = max(ans , ask2(p * 2 + 1 , l , r));
return ans;
}
}t1 , t2 , t3 , t4;
//t1:a全
//t2:b全
//t3:a正
//t4:a负
inline int read()
{
int asd = 0 , qwe = 1; char zxc;
while(!isdigit(zxc = getchar())) if(zxc == '-') qwe = -1;
while(isdigit(zxc)) asd = asd * 10 + zxc - '0' , zxc = getchar();
return asd * qwe;
}
inline bool ask1(int l , int r) { return (sum1[r] - sum1[l - 1]) == (r - l + 1); }
inline bool ask2(int l , int r) { return (sum2[r] - sum2[l - 1]) == (r - l + 1); }
inline bool ask3(int l , int r) { return (sum3[r] - sum3[l - 1]) == (r - l + 1); }
inline bool ask4(int l , int r) { return (sum4[r] - sum4[l - 1]) == (r - l + 1); }
signed main()
{
n = read() , m = read() , q = read();
for(int i = 1;i <= n;i++)
a[i] = read();
for(int i = 1;i <= m;i++)
b[i] = read();
for(int i = 1;i <= n;i++)
s[i] = a[i] , sum1[i] = sum1[i - 1] + (a[i] <= 0) , sum3[i] = sum3[i - 1] + (a[i] >= 0);
t1.build(1 , 1 , n);
for(int i = 1;i <= m;i++)
s[i] = b[i] , sum2[i] = sum2[i - 1] + (b[i] <= 0) , sum4[i] = sum4[i - 1] + (b[i] >= 0);
t2.build(1 , 1 , m);
for(int i = 1;i <= n;i++) s[i] = (a[i] >= 0 ? a[i] : 1e9); t3.build(1 , 1 , n);
for(int i = 1;i <= n;i++) s[i] = (a[i] < 0 ? a[i] : -1e9); t4.build(1 , 1 , n);
for(int i = 1;i <= q;i++)
{
int l1 = read() , r1 = read() , l2 = read() , r2 = read();
if(l1 > r1) swap(l1 , r2); if(l2 > r2) swap(l2 , r2);
if(ask1(l1 , r1))
cout << max(t1.ask2(1 , l1 , r1) * t2.ask2(1 , l2 , r2) , t1.ask1(1 , l1 , r1) * t2.ask2(1 , l2 , r2)) << endl;
else if(ask3(l1 , r1))
cout << max(t1.ask2(1 , l1 , r1) * t2.ask1(1 , l2 , r2) , t1.ask1(1 , l1 , r1) * t2.ask1(1 , l2 , r2)) << endl;
else if(ask2(l2 , r2))
cout << t1.ask1(1 , l1 , r1) * t2.ask2(1 , l2 , r2) << endl;
else if(ask4(l2 , r2))
cout << t1.ask2(1 , l1 , r1) * t2.ask1(1 , l2 , r2) << endl;
else
cout << max(t3.ask1(1 , l1 , r1) * t2.ask1(1 , l2 , r2) , t4.ask2(1 , l1 , r1) * t2.ask2(1 , l2 , r2)) << endl;
}
return 0;
}