嘘~ 正在从服务器偷取页面 . . .

洛谷P8818 [CSP-S 2022] 策略游戏 题解


洛谷P8818 [CSP-S 2022] 策略游戏 题解

题目链接:P8818 [CSP-S 2022] 策略游戏

题意

小 L 和小 Q 在玩一个策略游戏。

有一个长度为 \(n\) 的数组 \(A\) 和一个长度为 \(m\) 的数组 \(B\),在此基础上定义一个大小为 \(n \times m\) 的矩阵 \(C\),满足 \(C_{i j} = A_i \times B_j\)。所有下标均从 \(1\) 开始。

游戏一共会进行 \(q\) 轮,在每一轮游戏中,会事先给出 \(4\) 个参数 \(l_1, r_1, l_2, r_2\),满足 \(1 \le l_1 \le r_1 \le n\)\(1 \le l_2 \le r_2 \le m\)

游戏中,小 L 先选择一个 \(l_1 \sim r_1\) 之间的下标 \(x\),然后小 Q 选择一个 \(l_2 \sim r_2\) 之间的下标 \(y\)。定义这一轮游戏中二人的得分是 \(C_{x y}\)

小 L 的目标是使得这个得分尽可能大,小 Q 的目标是使得这个得分尽可能小。同时两人都是足够聪明的玩家,每次都会采用最优的策略。

请问:按照二人的最优策略,每轮游戏的得分分别是多少?

输入格式

第一行输入三个正整数 \(n, m, q\),分别表示数组 \(A\),数组 \(B\) 的长度和游戏轮数。

第二行:\(n\) 个整数,表示 \(A_i\),分别表示数组 \(A\) 的元素。

第三行:\(m\) 个整数,表示 \(B_i\),分别表示数组 \(B\) 的元素。

接下来 \(q\) 行,每行四个正整数,表示这一次游戏的 \(l_1, r_1, l_2, r_2\)

输出格式

输出共 \(q\) 行,每行一个整数,分别表示每一轮游戏中,小 L 和小 Q 在最优策略下的得分。

数据范围

对于所有数据,\(1 \le n, m, q \le {10}^5\)\(-{10}^9 \le A_i, B_i \le {10}^9\)。对于每轮游戏而言,\(1 \le l_1 \le r_1 \le n\)\(1 \le l_2 \le r_2 \le m\)

显然博弈论。但其实这是个很简单的博弈论+烦死人的数据结构。

注意到这里有很多种情况,那我们就全部考虑一遍。

不过通过列举其实可以发现,很多情况可以合并,详见代码即可。

总之我们需要维护「最小值」「最大值」「大于 \(0\) 的最小值」「大于 \(0\) 的次小值」「小于 \(0\) 的最大值」

时间复杂度 \(\mathcal{O}(n \log n)\)

考场代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
//#define INF 0x3f3f3f3f3f3f3f3f
#define INF 0x3f3f3f3f
typedef pair<int,int> pii;
#define Fi first
#define Se second
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
namespace FastIO
{
	#define gc() getchar()
	#define pc(a) putchar(a)
	template<typename T> void read(T &k)
	{
		char ch = gc(); T x = 0, f = 1;
		while(!isdigit(ch)) { if(ch == '-') f = -1; ch = gc(); }
		while(isdigit(ch)) { x = (x << 3) + (x << 1) + (ch ^ 48); ch = gc(); }
		k = x * f;
	}
	template<typename T> void write(T k)
	{
		if(k < 0) { k = -k; pc('-'); }
		static T stk[66]; T top = 0;
		do { stk[top++] = k % 10; k /= 10; }while(k);
		while(top) { pc(stk[--top] + '0'); }
	}
}using namespace FastIO;
#define N ((int)(1e5+15))
int n,m,Q,a[N],b[N];
namespace SubTask1
{
	int ta[1005], tb[1005];
	int solve(int l1,int r1,int l2,int r2)
	{
		int o = 0, len1 = r1 - l1 + 1, len2 = r2 - l2 + 1;
		for(int i=l1; i<=r1; i++) ta[++o] = a[i];
		o = 0;
		for(int i=l2; i<=r2; i++) tb[++o] = b[i];
		sort(ta + 1, ta + 1 + len1); sort(tb + 1, tb + 1 + len2);
		
		if(ta[1] > 0 || ta[len1] <= 0) // only > 0 or only <= 0
		{
			return max( min(ta[len1] * tb[len2], ta[len1] * tb[1]), 
					    min(ta[1] * tb[len2], ta[1] * tb[1]) );
		}else
		{
			if(tb[len2] <= 0) return ta[1] * tb[len2];
			if(tb[1] > 0) return ta[len1] * tb[1];
			int p = upper_bound(ta + 1, ta + 1 + len1, 0) - ta;
			int x = ta[p], y = ta[p-1]; assert(p-1 >= 1);
//			cout << x << ' ' << y << '\n';
			return max( min(x * tb[len2], x * tb[1]), 
					    min(y * tb[len2], y * tb[1]) );
		}
	}
	void main()
	{
		for(int l1,r1,l2,r2; Q--; )
		{
			read(l1); read(r1); read(l2); read(r2);
			write(solve(l1,r1,l2,r2)); pc('\n');
		}
		return;
	}
}
// ά»¤×îСֵ,×î´óÖµ,´óÓÚµÈÓÚ0µÄ×îСֵºÍ´ÎСֵ,СÓÚ0µÄ×î´óÖµ 
// 注:这一段其实是:最小值,最大值,大于0的最小值和次小值,小于0的最大值
// Dev C++ 真是个优质软件捏。
namespace SubTask2
{
	struct qry { int a,b,c,d,e; } tr[N * 4];
	#define ls(at) ( (at) << 1 )
	#define rs(at) ( (at) << 1 | 1 )
	int getsmn(int a,int b,int c,int d)
	{
		int tmp[4]={a,b,c,d};
		sort(tmp,tmp+4);
		for(int i=1; i<=3; i++) if(tmp[i] != tmp[0]) return tmp[i];
		return INF;
	}
	void push_up1(int at)
	{
		down(tr[at].a = tr[ls(at)].a, tr[rs(at)].a);
		up(tr[at].b = tr[ls(at)].b, tr[rs(at)].b );
		tr[at].c = min(tr[ls(at)].c, tr[rs(at)].c);
		tr[at].d = getsmn(tr[ls(at)].c, tr[rs(at)].c,tr[ls(at)].d, tr[rs(at)].d);
		up(tr[at].e = tr[ls(at)].e, tr[rs(at)].e);
	}
	void build1(int l,int r,int at)
	{
		if(l == r)
		{
			tr[at] = {a[l],a[l]};
			if(a[l] >= 0) tr[at].c = a[l];
			else tr[at].c = INF;
			tr[at].d = INF;
			if(a[l] <= 0) tr[at].e = a[l];
			else tr[at].e = -INF;
			return ;
		}
		int mid = (l + r) >> 1;
		build1(l,mid,ls(at)); build1(mid+1,r,rs(at));
		push_up1(at);
	}
	qry query1(int nl,int nr,int l,int r,int at)
	{
		if(nl <= l && r <= nr) return tr[at];
		int mid = (l + r) >> 1;
		if(nl > mid) return query1(nl,nr,mid+1,r,rs(at));
		if(nr <= mid) return query1(nl,nr,l,mid,ls(at));
		qry r1 = query1(nl,nr,l,mid,ls(at));
		qry r2 = query1(nl,nr,mid+1,r,rs(at));
		qry res = {min(r1.a, r2.a), max(r1.b, r2.b)};
		res.c = min(r1.c, r2.c);
		res.d = getsmn(r1.c,r2.c,r1.d,r2.d);
		res.e = max(r1.e,r2.e);
		return res;
	}
	pii ans[N * 4];
	void push_up2(int at)
	{
		down(ans[at].Fi = ans[ls(at)].Fi, ans[rs(at)].Fi);
		up(ans[at].Se = ans[ls(at)].Se, ans[rs(at)].Se);
	}
	void build2(int l,int r,int at)
	{
		if(l == r) return ans[at] = {b[l], b[l]}, void(0);
		int mid = (l + r) >> 1;
		build2(l,mid,ls(at)); build2(mid+1,r,rs(at));
		push_up2(at);
	}
	pii query2(int nl,int nr,int l,int r,int at)
	{
		if(nl <= l && r <= nr) return ans[at];
		int mid = (l + r) >> 1;
		if(nl > mid) return query2(nl,nr,mid+1,r,rs(at));
		if(nr <= mid) return query2(nl,nr,l,mid,ls(at));
		pii r1 = query2(nl,nr,l,mid,ls(at));
		pii r2 = query2(nl,nr,mid+1,r,rs(at));
		return {min(r1.Fi, r2.Fi), max(r1.Se, r2.Se)};
	}
    // 注:可以参考暴力部分,否则有亿点难理解
	int solve(int l1,int r1,int l2,int r2)
	{
		qry g = query1(l1,r1,1,n,1); pii d = query2(l2,r2,1,m,1);
		if(g.a > 0 || g.b <= 0)
		{
			return max( min(g.b * d.Se, g.b * d.Fi), 
					    min(g.a * d.Se, g.a * d.Fi) );
		}else
		{
			if(d.Se <= 0) return g.a * d.Se;
			if(d.Fi > 0) return g.b * d.Fi;
			return max({ min(g.c * d.Se, g.c * d.Fi), 
					    min(g.d * d.Se, g.d * d.Fi), 
						min(g.e * d.Se, g.e * d.Fi)} );
		}
	}
	void main()
	{
		build1(1,n,1); build2(1,m,1);
//		puts("---");
//		write(query2(1,m,1,m,1).Se); pc('\n');
//		puts("---");
		for(int l1,r1,l2,r2; Q--; )
		{
			read(l1); read(r1); read(l2); read(r2);
			write(solve(l1,r1,l2,r2)); pc('\n');
		}
		return;
	}
}

signed main()
{
//	ios::sync_with_stdio(0);
//	cin.tie(0); cout.tie(0);
	freopen("game.in","r",stdin);
	freopen("game.out","w",stdout);
	read(n); read(m); read(Q);
	for(int i=1; i<=n; i++) read(a[i]);
	for(int i=1; i<=m; i++) read(b[i]);
	
//	 if(n <= 1000 && m <= 1000 && Q <= 1000) SubTask1::main();
//	 else SubTask2::main();
	SubTask2::main();
	return 0;
}

文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录