[CSP-S 2022] 策略游戏

[CSP-S 2022] 策略游戏

题目传送门

题目分析

本文中 A 和 B 分别代表小 L 和小 Q,而原题中的 $A$,$B$ 两个数组在本题中分别用 $a$ 和 $b$ 表示。

矩阵这个描述就是障眼法。翻译一下题目:

A 在 $a[l_1 \cdots r_1]$ 中选择一个 $x$,然后 B 在 $b[l_2 \cdots r_2]$ 中选择一个 $y$,分数是 $x \times y$,A 想让分数尽可能大,B 想让分数尽可能小。求分数。

肯定先思考 B 再思考 A,因为 A 会思考 B 的思考。

B 的行为就是对于 $x$,找到一个 $b[l_2 \cdots r_2]$ 中的 $y$,使得 $x \times y$ 最小。

具体地:

  • $x \ge 0$ 时,B 会选择最小的 $y$;
  • $x < 0$ 时,B 会选择最大的 $y$。

那么 A 的行为是什么呢?还是按照正负分类讨论:

如果 A 这次想让 $x \ge 0$,那么 B 会选择最小的 $y$。如果这个 $y \ge 0$,那么 A 一定会选最大的 $x$;如果这个 $y < 0$,那么 A 一定会选最小的非负数 $x$(别忘了当前制约条件 $x \ge 0$)。

如果 A 这次想让 $x < 0$,那么 B 会选择最大的 $y$。如果这个 $y \ge 0$,那么 A 一定会选最大的负数 $x$;如果这个 $y <0$,那么 A 一定会选最小的 $x$。

因此 A 的行为只有四种:选择最大的 $x$;最小的 $x$,最大的负数 $x$,最小的非负数 $x$。

分别讨论 A 选择四种行为时 B 的选择,答案取最大值即可。

然后就变成了静态区间最值的板子。使用 6 个 ST 表分别存储以下信息:

  • $a$ 的区间最大值;
  • $a$ 的区间最小值;
  • $a$ 的负数区间最大值;
    • 具体是把所有满足 $a_i \ge 0$ 的 $a_i$ 全部替换为 $-\infty$ 代表这个位置不存在数,至于为何是 $-\infty$ 请读者自己思考。
  • $a$ 的非负数区间最小值;
    • 具体是把所有满足 $a_i < 0$ 的 $a_i$ 全部替换为 $+\infty$ 代表这个位置不存在数。
  • $b$ 的区间最大值;
  • $b$ 的区间最小值。

时间复杂度 $\mathcal{O}(n\log n + q)$。

代码实现

#include <bits/stdc++.h>

#define rint register int
#define int long long
#define endl '\n'

using namespace std;

const int N = 1e5 + 5;
const int M = 2e1 + 1;
const int maxinf = 1e18;
const int mininf = -1e18;

int n, m, q;
int maxx1[N][M], minn1[N][M];
int maxx2[N][M], minn2[N][M];
int maxx3[N][M], minn3[N][M];

int query_min(int l, int r, int *a)
{
	int k = log2(r - l + 1);
	return min(a[l * M + k], a[(r - (1 << k) + 1) * M + k]);
}

int query_max(int l, int r, int *a)
{
    int k = log2(r - l + 1);
    return max(a[l * M + k], a[(r - (1 << k) + 1) * M + k]); 
}

// maxx1: a 的区间最大值,   minn1: a 的区间最小值
// maxx2: a 的负数区间最大值,   minn2: a 的非负数区间最小值。
// maxx3: b 的区间最大值,       minn3: b 的区间最小值。

signed main() 
{
    cin >> n >> m >> q;
    
    for (rint i = 1; i <= n; i++) 
	{
        int k;
        cin >> k;
        maxx1[i][0] = minn1[i][0] = k;
        maxx2[i][0] = (k < 0 ? k : mininf);
        minn2[i][0] = (k >= 0 ? k : maxinf);
    }

    for (rint i = 1; i <= m; i++) 
	{
        int k;
        cin >> k;
        maxx3[i][0] = k;
		minn3[i][0] = k;
    }

    for (rint j = 1; j <= 21; j++) 
	{
        for (rint i = 1; i + (1 << j) - 1 <= n; ++i) 
		{
            int k = i + (1 << (j - 1));
            maxx1[i][j] = max(maxx1[i][j - 1], maxx1[k][j - 1]);
            maxx2[i][j] = max(maxx2[i][j - 1], maxx2[k][j - 1]);
            minn1[i][j] = min(minn1[i][j - 1], minn1[k][j - 1]);
            minn2[i][j] = min(minn2[i][j - 1], minn2[k][j - 1]);
        }
    }

    for (rint j = 1; j <= 21; j++) 
	{
        for (rint i = 1; i + (1 << j) - 1 <= m; i++) 
		{
            int k = i + (1 << (j - 1));
            maxx3[i][j] = max(maxx3[i][j - 1], maxx3[k][j - 1]);
            minn3[i][j] = min(minn3[i][j - 1], minn3[k][j - 1]);
        }
    }

    while (q--) 
	{
        int l1, r1, l2, r2;
        cin >> l1 >> r1 >> l2 >> r2;
        
        int amax = query_max(l1, r1, (int *)maxx1);
        int amin = query_min(l1, r1, (int *)minn1);
        int afmx = query_max(l1, r1, (int *)maxx2);
        int afmn = query_min(l1, r1, (int *)minn2);
        int bmax = query_max(l2, r2, (int *)maxx3);
        int bmin = query_min(l2, r2, (int *)minn3);

        int ans = mininf;

        ans = max(ans, amax * (amax >= 0 ? bmin : bmax));
        ans = max(ans, amin * (amin >= 0 ? bmin : bmax));
        
        if (afmx != mininf)
        {
			ans = max(ans, afmx * (afmx >= 0 ? bmin : bmax));
		}
        if (afmn != maxinf)
        {
			ans = max(ans, afmn * (afmn >= 0 ? bmin : bmax));
		}
		
        cout << ans << endl;
    }
    
    return 0;
}
posted @ 2023-10-14 18:34  南松的科技树  阅读(179)  评论(0)    收藏  举报