这道题不止一种做法,我用的是其中一种:贪心+RMQ
## 贪心的思路:
### 1.小L如果有正有负,分为三种情况:
#### (1)小Q只有正,这种情况对小Q不利,小L选择最大的正数(这里包括0,下同),小Q被逼的死路一条,只有选择最小的正数可以尽量实得分更小。
#### (2)小Q只有负,这种情况也对小Q不利,由于负负得正,小L选择绝对值最大的负数(相当于最小的负数),小Q没办法,只好选择绝对值最小的负数(相当于最大的负数),可以尽量实得分更小。
#### (3)小Q有正有负,这种情况对小Q有利,为何?因为小L出正,小Q出负,小L出负,小Q出正,小L不管怎么出,小Q都有办法使得它<=0,小L只能在出正或出负的情况选一个最小的,小L的第一个选择(正数),应选最小的正数,小Q就选最小的负数。小L的第二个选择(负数),应选最大的负数,小Q选最大的正数。
### 2.小L只有负,分为两种情况:
#### (1)小Q只有负,这种情况对小Q不利,由于负负得正,小L选择绝对值最大的负数(相当于最小的负数),小Q没办法,只好选择绝对值最小的负数(相当于最大的负数),可以尽量实得分更小。
#### (2)小Q只有正或有正有负,这种情况对小Q有利,小L没办法,选绝对值最小的负数(相当于最大的负数),小Q选择最大的正数。
### 3.小L只有正,分为两种情况:
#### (1)小Q只有正,这种情况对小Q不利,小L选择最大的正数,小Q选最小的正数。
#### (2)小Q只有负或有正有负,这种情况对小Q有利,小L没办法,只好选择最小的的正数,小Q选择绝对值最大的负数(相当于最小的负数)。
## 代码部分:
### 准备八个数组:fza,fza1,fzb,fzb1,ffa,ffa1,ffb,ffb1,分别代表小L正数最大值、小L正数最小值、小Q正数最大值、小Q正数最小值、小L负数最大值、小L负数最小值、小Q负数最大值、小Q负数最小值。
### 初始化:属于这个部分的(负数或正数)就直接赋值,否则是大的就-1e10,小的就1e10。
### 剩下的就按思路做。
### 代码:
#include<bits/stdc++.h> using namespace std; #define int long long const int N = 1e5; int a[N]; int b[N]; int fza[N][20]; int fza1[N][20]; int fzb[N][20]; int fzb1[N][20]; int ffa[N][20]; int ffa1[N][20]; int ffb[N][20]; int ffb1[N][20]; int n,q,m; signed main() { scanf("%lld %lld %lld",&n,&m,&q); for(int i = 1;i<=n;i++) { scanf("%lld",&a[i]); fza[i][0] = a[i]>=0?a[i]:(int)-1e10; fza1[i][0] = a[i]>=0?a[i]:(int)1e10; ffa[i][0] = a[i]<0?a[i]:(int)-1e10; ffa1[i][0] = a[i]<0?a[i]:(int)1e10; } for(int i = 1;i<=m;i++) { scanf("%lld",&b[i]); fzb[i][0] = b[i]>=0?b[i]:(int)-1e10; fzb1[i][0] = b[i]>=0?b[i]:(int)1e10; ffb[i][0] = b[i]<0?b[i]:(int)-1e10; ffb1[i][0] = b[i]<0?b[i]:(int)1e10; } for(int i = 1;i<=log2(n);i++) { for(int j = 1;j<=n-(1<<i)+1;j++) { fza[j][i] = max(fza[j][i-1],fza[j+(1<<(i-1))][i-1]); fza1[j][i] = min(fza1[j][i-1],fza1[j+(1<<(i-1))][i-1]); ffa[j][i] = max(ffa[j][i-1],ffa[j+(1<<(i-1))][i-1]); ffa1[j][i] = min(ffa1[j][i-1],ffa1[j+(1<<(i-1))][i-1]); } } for(int i = 1;i<=log2(m);i++) { for(int j = 1;j<=m-(1<<i)+1;j++) { fzb[j][i] = max(fzb[j][i-1],fzb[j+(1<<(i-1))][i-1]); fzb1[j][i] = min(fzb1[j][i-1],fzb1[j+(1<<(i-1))][i-1]); ffb[j][i] = max(ffb[j][i-1],ffb[j+(1<<(i-1))][i-1]); ffb1[j][i] = min(ffb1[j][i-1],ffb1[j+(1<<(i-1))][i-1]); } } for(int i = 1;i<=q;i++) { int la,ra,lb,rb; scanf("%lld %lld %lld %lld",&la,&ra,&lb,&rb); int kl = log2(ra-la+1); int num; int kr = log2(rb-lb+1); if(max(fza[la][kl],fza[ra-(1<<kl)+1][kl])!=(int)-1e10&&max(ffa[la][kl],ffa[ra-(1<<kl)+1][kl])!=(int)-1e10) { if(max(ffb[lb][kr],ffb[rb-(1<<kr)+1][kr]) == (int)-1e10) { num = max(fza[la][kl],fza[ra-(1<<kl)+1][kl])*min(fzb1[lb][kr],fzb1[rb-(1<<kr)+1][kr]); } if(max(fzb[lb][kr],fzb[rb-(1<<kr)+1][kr]) == (int)-1e10) { num = min(ffa1[la][kl],ffa1[ra-(1<<kl)+1][kl])*max(ffb[lb][kr],ffb[rb-(1<<kr)+1][kr]); } if(max(fzb[lb][kr],fzb[rb-(1<<kr)+1][kr])!=(int)-1e10&&max(ffb[lb][kr],ffb[rb-(1<<kr)+1][kr])!=(int)-1e10) { int L1 = min(fza1[la][kl],fza1[ra-(1<<kl)+1][kl])*min(ffb1[lb][kr],ffb1[rb-(1<<kr)+1][kr]),L2 = max(ffa[la][kl],ffa[ra-(1<<kl)+1][kl])*max(fzb[lb][kr],fzb[rb-(1<<kr)+1][kr]); num = max(L1,L2); } } if(max(fza[la][kl],fza[ra-(1<<kl)+1][kl]) == (int)-1e10&&max(ffa[la][kl],ffa[ra-(1<<kl)+1][kl])!=(int)-1e10) { if(max(fzb[lb][kr],fzb[rb-(1<<kr)+1][kr]) == (int)-1e10&&max(ffb[lb][kr],ffb[rb-(1<<kr)+1][kr])!=(int)-1e10) { num = min(ffa1[la][kl],ffa1[ra-(1<<kl)+1][kl])*max(ffb[lb][kr],ffb[rb-(1<<kr)+1][kr]); } else { num = max(ffa[la][kl],ffa[ra-(1<<kl)+1][kl])*max(fzb[lb][kr],fzb[rb-(1<<kr)+1][kr]); } } if(max(fza[la][kl],fza[ra-(1<<kl)+1][kl])!=(int)-1e10&&max(ffa[la][kl],ffa[ra-(1<<kl)+1][kl]) == (int)-1e10) { if(max(fzb[lb][kr],fzb[rb-(1<<kr)+1][kr])!=(int)-1e10&&max(ffb[lb][kr],ffb[rb-(1<<kr)+1][kr]) == (int)-1e10) { num = max(fza[la][kl],fza[ra-(1<<kl)+1][kl])*min(fzb1[lb][kr],fzb1[rb-(1<<kr)+1][kr]); } else { num = min(fza1[la][kl],fza1[ra-(1<<kl)+1][kl])*min(ffb1[lb][kr],ffb1[rb-(1<<kr)+1][kr]); } } printf("%lld\n",num); } return 0; }
题目3782 [CSP 2022S]策略游戏
AAAAAAAAAAAAAAAAAAAA
8
评论
2024-01-06 19:03:41
|
|
先吐槽下COGS评测机,洛谷AC,这里不开O2会T( 以下转载于我的洛谷博客:
赛场切了很开心,于是写篇题解。
看到题后我们可以想一下,每次两人可能进行的操作会是什么。
如果第一个人选了正数,第二个人首先会选最小的负数(前提是能选到负数),没有负数第二个人会选 $0$,没有 $0$ 就会选最小的正数。
如果第一个人选了负数,第二个人会选最大的正数,没有正数第二个人会选 $0$,没有 $0$ 会选最小的负数。
如果第一个人选 $0$,第二个人无论怎么选,答案都是 $0$。
那么,我们就可以让第一个人根据第二个人有的数字来确定自己选什么。
分情况讨论,如果第一个人有 $0$,先让答案等于 $0$。
如果第一个人有正数,结合上面的分析,可以得出,答案可能是第一个人的最小正数乘以第二个人的最大负数、第一个人的最大正数乘以第二个人的最小正数、$0$。
负数同理,可能是第一个人的最大负数乘以第二个人的最大正数、第一个人的最小负数乘以第二个人的最大负数、$0$。
那么这个题目就转化为求两个区间的最大正数、最小正数、最大负数、最小负数和是否有 $0$。
最常见的方法是写个线段树,但是由于我线段树太烂~~以及很喜欢分块~~,我最后写了分块。后来我发现,线段树的做法其实比分块要简单太多,我写的分块的码量几乎是别人线段树的两倍。
另外,我发现我打的分块求是否有 $0$ 出了很多奇怪的错误,于是求是否有 $0$ 我使用了前缀和。
以下是带注释的代码:
#include <bits/stdc++.h> using namespace std; #define int long long const int MAXN = 100010; int n, m, q, s, a[MAXN], b[MAXN]; int maxn1a[500], maxn2a[500], minn1a[500], minn2a[500], is0a[MAXN]; // 分块,对应序列 a int maxn1b[500], maxn2b[500], minn1b[500], minn2b[500], is0b[MAXN]; // 序列 b struct node { int maxn1, maxn2, minn1, minn2, is0; }; // 分别对应最大的正数、最大的负数、最小的正数、最小的负数、是否有 0 node query1(int x, int y) { // 处理序列 a int ka = x / s, kb = y / s; node ans; ans.maxn2 = -0x3f3f3f3f3f3f3f3f; ans.minn1 = 0x3f3f3f3f3f3f3f3f; ans.maxn1 = 0; ans.minn2 = 0; if(ka == kb) { for(int i = x; i <= y; i ++) { if(a[i] < 0) { ans.maxn2 = max(ans.maxn2, a[i]); ans.minn2 = min(ans.minn2, a[i]); } if(a[i] > 0) { ans.maxn1 = max(ans.maxn1, a[i]); ans.minn1 = min(ans.minn1, a[i]); } } return ans; } for(int i = x; i < (ka + 1) * s; i ++) { if(a[i] < 0) { ans.maxn2 = max(ans.maxn2, a[i]); ans.minn2 = min(ans.minn2, a[i]); } if(a[i] > 0) { ans.maxn1 = max(ans.maxn1, a[i]); ans.minn1 = min(ans.minn1, a[i]); } } for(int i = kb * s; i <= y; i ++) { if(a[i] < 0) { ans.maxn2 = max(ans.maxn2, a[i]); ans.minn2 = min(ans.minn2, a[i]); } if(a[i] > 0) { ans.maxn1 = max(ans.maxn1, a[i]); ans.minn1 = min(ans.minn1, a[i]); } } for(int i = ka + 1; i < kb; i ++) { ans.maxn2 = max(ans.maxn2, maxn2a[i]); ans.minn2 = min(ans.minn2, minn2a[i]); ans.maxn1 = max(ans.maxn1, maxn1a[i]); ans.minn1 = min(ans.minn1, minn1a[i]); } return ans; } node query2(int x, int y) { // 序列 b int ka = x / s, kb = y / s; node ans; ans.maxn2 = -0x3f3f3f3f3f3f3f3f; ans.minn1 = 0x3f3f3f3f3f3f3f3f; ans.maxn1 = 0; ans.minn2 = 0; if(ka == kb) { for(int i = x; i <= y; i ++) { if(b[i] < 0) { ans.maxn2 = max(ans.maxn2, b[i]); ans.minn2 = min(ans.minn2, b[i]); } if(b[i] > 0) { ans.maxn1 = max(ans.maxn1, b[i]); ans.minn1 = min(ans.minn1, b[i]); } } return ans; } for(int i = x; i < (ka + 1) * s; i ++) { if(b[i] < 0) { ans.maxn2 = max(ans.maxn2, b[i]); ans.minn2 = min(ans.minn2, b[i]); } if(b[i] > 0) { ans.maxn1 = max(ans.maxn1, b[i]); ans.minn1 = min(ans.minn1, b[i]); } } for(int i = kb * s; i <= y; i ++) { if(b[i] < 0) { ans.maxn2 = max(ans.maxn2, b[i]); ans.minn2 = min(ans.minn2, b[i]); } if(b[i] > 0) { ans.maxn1 = max(ans.maxn1, b[i]); ans.minn1 = min(ans.minn1, b[i]); } } for(int i = ka + 1; i < kb; i ++) { ans.maxn2 = max(ans.maxn2, maxn2b[i]); ans.minn2 = min(ans.minn2, minn2b[i]); ans.maxn1 = max(ans.maxn1, maxn1b[i]); ans.minn1 = min(ans.minn1, minn1b[i]); } return ans; } signed main() { cin >> n >> m >> q; s = sqrt(n); memset(maxn2a, -0x3f, sizeof(maxn2a)); memset(minn1a, 0x3f, sizeof(minn1a)); memset(maxn2b, -0x3f, sizeof(maxn2b)); memset(minn1b, 0x3f, sizeof(minn1b)); for(int i = 1; i <= n; i ++) { cin >> a[i]; is0a[i] = is0a[i - 1]; // 前缀和处理是否有 0 if(a[i] == 0) { is0a[i] ++; } if(a[i] < 0) { maxn2a[i / s] = max(maxn2a[i / s], a[i]); // 处理每一块的最大最小值 minn2a[i / s] = min(minn2a[i / s], a[i]); } if(a[i] > 0) { maxn1a[i / s] = max(maxn1a[i / s], a[i]); minn1a[i / s] = min(minn1a[i / s], a[i]); } } for(int i = 1; i <= m; i ++) { cin >> b[i]; is0b[i] = is0b[i - 1]; if(b[i] == 0) { is0b[i] ++; } if(b[i] < 0) { maxn2b[i / s] = max(maxn2b[i / s], b[i]); // 同上 minn2b[i / s] = min(minn2b[i / s], b[i]); } if(b[i] > 0) { maxn1b[i / s] = max(maxn1b[i / s], b[i]); minn1b[i / s] = min(minn1b[i / s], b[i]); } } for(int i = 1; i <= q; i ++) { int l1, l2, r1, r2, ans = -0x3f3f3f3f3f3f3f3f; cin >> l1 >> r1 >> l2 >> r2; node ans1 = query1(l1, r1); ans1.is0 = is0a[r1] - is0a[l1 - 1]; node ans2 = query2(l2, r2); ans2.is0 = is0b[r2] - is0b[l2 - 1]; // 分别求两个区间 if(ans1.is0) { ans = 0; // 特判序列 a中有 0 } if(ans1.maxn1) { // 分析中的各种情况分别处理 if(ans2.minn2) { ans = max(ans, ans1.minn1 * ans2.minn2); } else if(ans2.is0) { ans = max(ans, (int)0); } else { ans = max(ans, ans1.maxn1 * ans2.minn1); } } if(ans1.minn2) { if(ans2.maxn1) { ans = max(ans, ans1.maxn2 * ans2.maxn1); } else if(ans2.is0) { ans = max(ans, (int)0); } else { ans = max(ans, ans1.minn2 * ans2.maxn2); } } cout << ans << endl; } return 0; }
题目3782 [CSP 2022S]策略游戏
AAAAAAAAAAAAAAAAAAAA
11
评论
2022-11-06 13:47:26
|