2023-1018 CSP 模拟赛题解

A. 奶牛杀

题面

题目描述

贝茜和她的朋友们正在玩奶牛杀。在这款游戏中,贝茜本色出演奶牛,她的 nn 个朋友扮演猎人。猎人从 11nn 编号,第 ii 个猎人有血量 aia_i 和攻击力 bib_i。且第 ii 个猎人瞄准 imodn+1i \bmod n+1 个猎人。当第 ii 个猎人血量小于等于 00时,会立刻向第 imodn+1i\bmod n+1 个猎人开枪,使之血量减少 bib_i 点。如果它也血量变为了小于等于 00 则也会开枪,有可能造成连锁开枪。

需要注意的是,本游戏并无缩圈环节,若第 imodn+1i \bmod n+1 个猎人已经退出游戏,ii 号会打空而非向后寻找人开枪。

在奶牛杀中,没有投票环节,只有贝茜每晚上袭击一个猎人,造成 11 点伤害。她现在想知道最快需要几个晚上才能杀死所有猎人?

输入格式

第一行一个正整数 nn表示猎人总数。

接下来的 nn行,第行包含两个正整数 ai,bia_i,b_i,分别表示第 ii个猎人的血量和攻击力。

输出格式

输出一个正整数,表示贝茜最快需要多少个晚上获得胜利。

输入输出样例

样例输入 #1

3
7 15
2 14
5 3

样例输出 #1

6

样例解释 #1

最佳策略是连续 22 个晚上攻击猎人 22,其死亡时的射击导致猎人 33 死亡,猎人 33 死亡时的射击对猎人 11 造成
33 点伤害,接下来只需要连续 44 个晚上攻击猎人 11 即可。

数据范围与约定

  • 对于 10%10\% 的数据,保证 n10n\leq 10
  • 对于另外 10%10\% 的数据,保证 n100n\leq 100
  • 对于另外 20%20\% 的数据,保证 n1000n\leq 1000
  • 对于另外 20%20\% 的数据,保证 biai mod n+1b_i\leq a_{i\ mod\ n+1}
  • 对于 100%100\% 的数据,保证 n3105,1ai,bi1012n\leq 3*10^5,1\leq a_i,b_i\leq 10^{12}

题意简述

给定 nn 个人,以及其生命值 aia_i,这 nn 个人之间的关系构成一个环,每个人死亡时会对下一个人造成 bib_i 的伤害,你可以选择任意数量的人造成任意伤害,求为了使所有人死亡你所需要造成的伤害

思路

为了方便,我们把每个人的下一个人记为 nxtinxt_i,上一个人记为 preipre_i

假设我们从第 kk 个人开始造成伤害,那么杀掉第一个人所需要的伤害为 aka_k,对于其余所有人,如果他的上一个人可以直接杀掉他,那么我们就不做干预,否则就另外造成伤害

那么显然以第 kk 个人开始的所需要的伤害为:

max{bprekak,0}+ak+i=1nmax{bpreiai,0}-\max{ \{b_{pre_k} - a_k, 0\} } + a_k+ \sum \limits_{i = 1}^{n} \max{ \{b_{pre_i} - a_i, 0\} }

那么计算出 sum=i=1nmax{bpreiai,0}sum = \sum \limits_{i = 1}^{n} \max{ \{b_{pre_i} - a_i, 0\} },枚举每个位置更新答案即可

总的时间复杂度为 O(n)O(n)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <bits/stdc++.h>
namespace solution
{
typedef long long LL;
const int MAXN = 3e5 + 5;
const LL INF = 0x3f3f3f3f3f3f3f3fLL;
int n;
LL sum = 0;
LL ans = INF;
LL a[MAXN], b[MAXN], c[MAXN];
int main()
{
freopen("doge.in", "r", stdin);
freopen("doge.out", "w", stdout);
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
scanf("%lld%lld", &a[i], &b[i]);
}
for (int i = 1; i <= n; i++)
{
c[i % n + 1] = std::max(0LL, a[i % n + 1] - b[i]);
sum += c[i % n + 1];
}
for (int i = 1; i <= n; i++)
{
ans = std::min(ans, sum - c[i] + a[i]);
}
printf("%lld\n", ans);
return 0;
}
}
int main()
{
solution::main();
return 0;
}

B. 备餐

题面

题目描述

约翰要给他的奶牛们备餐,有 nn种食物,每种食物有满意度 aia_i。约翰将准备食物的一个非空子序列给奶牛。

奶牛们对食物的要求也很高,他们要求从准备的食物中任选两个计算他们满意度的异或,如果小于他们的忍耐度 xx就会生气。

约翰想问问你,在所有 2n12^n-1 个非空子序列中,有多少种序列满足,“任选两种食物,满意度的异或和大于等于奶牛的忍耐度 xx”?你需要输出方案数模 998244353998244353 之后的结果。

如果子序列只有一个元素,约翰认为一定是合法的。

输入格式

第一行包含两个非负整数 n,xn,x 表示食物的种类数和奶牛们的忍耐度。

第二行包含 nn 个非负整数 a1,a2,...ana_1,a_2,...a_n 表示每种食物的满意度。

输出格式

输出一个非负整数,表示方案数模 998244353998244353 的结果。

输入输出样例

样例输入 #1

3 0 0 1 2

样例输出 #1

7

样例解释 #1

所有的 2312^3-1 个非空子序列都满足条件

样例输入 #2

3 2 0 1 2

样例输出 #2

5

样例解释 #2

由于 01=1<20 \bigoplus 1=1<2,子序列 {0,1}\{0,1\} 和子序列 {0,1,2}\{0, 1, 2\} 不满足条件。

样例输入 #3

7 4 11 5 5 8 3 1 3

样例输出 #3

35

样例解释 #3

不保证 aia_i 互不相同,选取不同的 5533,被认为是不同的方案。

数据范围与约定

  • 对于 100%100\% 的数据,保证 1n31051\leq n \leq 3*10^50ai,x<2600\leq a_i,x< 2^{60}

C. 约翰:奶牛杀手

题面

题目描述

约翰最近迷恋上了《只狼:影逝二度》,于是他决定拿奶牛试试手。

具体来说,有 nn个奶牛站在一个数轴上,每个奶牛有一个位置 xix_i,还有一个警戒范围 [li,ri][l_i,r_i]

刚开始所有奶牛都是未警戒的状态。约翰可以偷袭并瞬间杀死一个未警戒状态的奶牛 ii,但此时所有 xi[lj,rj]x_i\in[l_j,r_j] 的奶牛
jj 都会进入警戒状态并前来杀死小W,即满足i的位置位于j的警戒范围内的j。其他奶牛则会保持未警戒状态。

约翰有一次复活机会,复活后仍然活着的奶牛会恢复到未警戒状态和原来的位置。但是如果复活后偷袭被别的奶牛再次被发现就会失败。

约翰有 TT 个农场,他想问问你能否只使用偷袭和复活来通关每张地图。进入每张地图时复活次数会重置为1。

输入格式

输入的第一行包含一个正整数 TT,表示地图总数。

接下来连续输入 TT 张地图的信息。

每张地图的第一行包含一个正整数 nn,表示这张地图中的奶牛总数。

此后的 nn 行,每行包含三个正整数 xi,li,rix_i, l_i, r_i,表示第 ii 头奶牛的位置和警戒范围。保证奶牛按照坐标严格升序给出,即 x1<x2<...<xnx_1 < x_2 < ... < x_n

输出格式

输出 TT 行,每行包含一个字符串 YesNo,分别表示约翰是否可以通过这张地图。

输入输出样例

样例输入 #1

2
4
3 1 5
6 2 10
7 4 10
9 8 11
3
1 1 3
2 1 3
3 1 3

样例输出 #1

Yes
No

样例解释 #1

对于第一张地图,约翰可以首先对第二个奶牛(位置为 6)发动偷袭,并会被第三个奶牛杀死。在复生后,他可以按照三,一,四的顺序分别杀死剩余的三个奶牛,并且不会引起敌人的警觉。
对于第二张地图,可以发现无论如何安排顺序,约翰都被顺序中最后 的那个敌人杀死两次,用完所有的复生机会。

数据范围与约定

对于 100%100\% 的数据,保证 1T105,1n106,n3106,1lixiri109,1x1<x2<...<xn1091\leq T\leq 10^5,1\leq n \leq 10^6,\sum n\leq 3*10^6,1\leq l_i\leq x_i\leq r_i\leq 10^9,1\leq x_1<x_2<...<x_n\leq 10^9

题意简述

给定 nn 个区间,每个区间包含三个信息:xi,li,rix_i, l_i, r_i,求判断能否在删除某个区间之后,可以通过每次选择一个“ xix_i 不属于任何其他未删除的区间的 lj,rjl_j, r_j 的区间”删除掉,使得所有区间最后都被删除

暴力做法

简化题意,可以通过相互包含的关系建出一张有向图,图上有环时则无法处理,只能将某个区间删除掉

那么可以通过枚举环上的点删除,拓扑排序判断图中是否还有其他环的方法来解决,时间复杂度为 O(n3)O(n ^ 3)

可以发现如下性质:图中如果存在环,那么必定存在二元环,

以三元环为例,假设图中有三元环 a,b,ca, b, c,不妨设 xa<xb<xcx_a < x_b < x_c,显然 bb 可以在 aa 的区间内,cc 可以在 bb 的区间内,又因为构成环,那么 aabb 必定有一个在 cc 的区间内,显然无论哪种情况,图中都有二元环,故命题得证

那么现在的问题就变成了在图中找二元环的问题

首先可以在图中找出任意一对二元环,对环上的两个点枚举删除,判断图中是否还有其他二元环

判断二元环时可以从左向右枚举每个点,同时维护一个栈,栈中的元素从栈底到栈顶 xx 递增,rr 递减,xx 递增保证了栈中元素最可能被包含在其他区间中,rr 递减保证了区间中的元素最可能包含其他元素,那么对于每个当前枚举到的元素 ii,一直弹出栈中元素,直到栈中元素的 rr 大于 iirr 为止,对于弹出的每个元素以及最后的栈顶,判断其与 ii 是否构成二元环,如果枚举完了都没有找到,说明图中不存在二元环 ,也就说明原图为有向无环图

下面考虑证明正确性

假设图中存在二元环 a,ba, b,不妨设 ax<bxa_x < b_x,那么一定有 albxara_l \le b_x \le a_r,分两以下几种情况讨论:

  • 枚举到 bbaa 未被弹出栈, 此时要么枚举到 bb 时与 aa 进行判断,发现 a,ba, b 的环,要么枚举到后边某个位置 cc 时,发现一个更大的环
  • 枚举到 bbaa 已经出栈,说明在前边某个位置 ccaa 已经和 cc 构成环

故若图中有环,那么该算法一定可以找到

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include <bits/stdc++.h>
namespace solution
{
typedef long long LL;
const int MAXN = 3e6 + 5;
int t;
LL n;
LL a[MAXN];
struct Node
{
LL l, x, r;
};
Node node[MAXN];
auto find(int pos)
{
std::stack<int> stk;
for (int i = 1; i <= n; i++)
{
if (i == pos)
{
continue;
}
while (!stk.empty())
{
int u = stk.top();
if (node[i].l <= node[u].x && node[u].x <= node[i].r && node[u].l <= node[i].x && node[i].x <= node[u].r)
{
return std::make_pair(i, u);
}
if (node[u].r > node[i].r)
{
break;
}
stk.pop();
}
stk.push(i);
}
return std::make_pair(0, 0);
}
int main()
{
freopen("sekiro.in", "r", stdin);
freopen("sekiro.out", "w", stdout);
scanf("%d", &t);
while (t--)
{
scanf("%lld", &n);
for (int i = 1; i <= n; i++)
{
scanf("%lld%lld%lld", &node[i].x, &node[i].l, &node[i].r);
}
auto t = find(0);
if (t == std::make_pair(0, 0) || find(t.first) == std::make_pair(0, 0) || find(t.second) == std::make_pair(0, 0))
{
puts("Yes");
}
else
{
puts("No");
}
}
return 0;
}
}
int main()
{
solution::main();
return 0;
}