E. 安 (思维/贪心) 时间复杂度 :$O(n)$ 题意 $May$ 和 $Ray$ 分别拥有一支有 $n$ 个骑士的军队,第 $i$ 个骑士的血量分别为 $a_i$ 和 $b_i$ 。每次操作可以选择下标 $i$ ,指挥自己的军队去攻击对方下标为 $i$ 的军队,将对方骑士的血量值减一,当血量值为零时,该骑士被消灭。由 $May$ 作为先手,两人轮流交替操作,都会最大化自己幸存的骑士数量。请问最后$May$ 能幸存多少支骑士?
数据范围
$1 \le n \le 10^5 , \sum n \le 10^5$
$1 \le a_i,b_i \le 10^9$
思路 从贪心的角度考虑,当$May$ 作为先手时,设 $May$ 最终幸存的骑士数量为 $cnt$ ,由于攻击对象只会是下标相同的骑士,分三种情况考虑:
当$a_i < b_i$ 的军队,$May$ 的攻击”无效”,因为 $Ray$ 作为后手一定会偿还伤害,无法摆脱 $a_i < b_i$ 的命运
当$a_i>b_i$ 的军队,即使 $May$ 是后手操作,也会同上种情况击败 $Ray$ , 此时 $cnt$ ++
当$a_i=b_i$的军队,不难想到,此时先手操作便决定了最终的胜负,两人交替时一定会优先选择$a_i = b_i$的军队进行攻击,若 $a_i=b_i$ 的军队数量为$x$ , 则 $cnt+=\lceil \dfrac{x}{2} \rceil$ .
Code: 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 #include<bits/stdc++.h> #define endl '\n' using i64 = long long; using namespace std; signed main(){ std::ios::sync_with_stdio(false);cin.tie(0); int t;cin>>t; while(t--){ int n; cin >> n; vector<int> a(n + 1),b(n+1); for (int i = 1; i <= n;i++) cin >> a[i]; for (int i = 1; i <= n; i++) cin >> b[i]; int cnt1 = 0, cnt2 = 0; for (int i = 1; i <= n;i++) if(a[i]>b[i]) cnt1++; else if(a[i]==b[i]) cnt2++; cout << cnt1 + (cnt2 + 1) / 2 << endl; } return 0; }
L. 知 (数学) 时间复杂度 :$O(T \times n^2)$ 题意 游戏有 $n$ 个级别,已知通过每个级别的概率分别为$\frac{a_1}{100},\frac{a_2}{100}…\frac{a_n}{100}$.
可以执行任意数量的操作,每次操作允许选择一个索引 $i (1 \le i < n,a_i<100,a_{i+1}>0)$ ,使 $a_i$ 的值加一, $a_{i+1}$ 的值减一。请问最后可能得最大通关概率是多少,输出该答案乘以 $100^n$ 并模上 $998244353$ 的结果。
数据范围
思路 由数学知识易得,题意求的是 $a_1 \times a_2 \times … \times a_n$ 的最大值,每次修改操作,两者的和是固定不变的!而根据均值不等式的性质可知,两者的和一定时,当且仅当两者相等时乘积最大,数值越接近,两数乘积一定越大。
所以每次操作,我们尽可能地将两者数值”抹平”以缩小差值,然而操作只能针对相邻前后两个,不难想到,我们不断地进行遍历,每次将相邻两者“抹平”,直至最终无法操作,进而形成一个非递增序列。而由于$n$ 的范围极小,最坏情况$O(n^2)$ 也能够通过 .
由于要形成非递增序列,显然,当 $a_i$ 与 $a_{i+1}$ 前后两者起始值分别为 $x$ 和 $y$ 时,有:
$a_i=a_{i+1}= \dfrac{x+y}{2} \:\:\:\:\:\: a_i与a_{i+1}同奇偶 $ $a_i=\lceil \dfrac{x+y}{2} \rceil ,a_{i+1}=\lfloor \dfrac{x+y}{2} \rfloor \:\:\:\:\:\:a_i与a_{i+1}奇偶不同$
使用while循环体进行若干次遍历,每次遍历完成后判断一遍该序列是否是非递增,最后计算结果记得开 $long \:\: long$ 并进行取模操作.
Code: 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 #include<bits/stdc++.h> #define endl '\n' using i64 = long long; using namespace std; const i64 p = 998244353; signed main() { std::ios::sync_with_stdio(false);cin.tie(0); int t; cin >> t; while(t--){ int n; cin >> n; vector<int> a(n + 1); for (int i = 1; i <= n; i++) cin >> a[i]; auto check_sorted = [&]() -> bool { for (int i = 2; i <= n; i++) if (a[i] > a[i - 1]) return 0; return 1; }; while(!check_sorted()){ for (int i = 1; i < n;i++){ if (a[i] >= a[i + 1]) continue; int dis = a[i + 1] - a[i]; if(dis&1){ a[i] += dis / 2 + 1; a[i + 1] -= (dis / 2 + 1); }else{ a[i] += dis / 2; a[i + 1] -= dis / 2; } } } i64 ans = 1; for (int i = 1; i <= n;i++) ans = (ans * a[i]) % p; cout << ans << endl; } return 0; }
B. 珑 (模拟) 时间复杂度 :$O(T)$ 题意 现在有一个 $n \times m$ 的矩形,使用各种 $1 \times 2$ 或 $2 \times 1$ 的骨牌。每个位置都必须被完整地覆盖,并且放置骨牌不能超出边界.
另外,有两种类型的限制:
骨牌的短边不能相邻,即边长为1的边不相邻
骨牌的长边不能相邻,即边长为2的边不相邻
给定$n,m,a,b$ ,代表矩形的尺寸和是否存在限制,当 $a=0/1$ 时,表示该限制存在/不存在,$b$ 同理。请问最终能否实现完全覆盖?
数据范围
$1 \le T \le 10^5$
$1 \le n,m \le 10^9$
$0 \le a,b \le 1$
思路 没有什么思维难度,列举各类情况分类讨论以及特殊情况特判即可 …
Code: 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 #include<bits/stdc++.h> #define endl '\n' using i64 = long long; using namespace std; int main(){ ios::sync_with_stdio(false); int t;cin>>t; while(t--){ int n, m, a, b; cin >> n >> m >> a >> b; if(n+m==3){ cout << "Yes" << endl; continue; } if((n & 1) && (m & 1)){ cout << "No" << endl; continue; } if(b == 0 && a == 1){ if(n == 1 || m == 1){ cout<< "Yes" <<endl; continue; } else{ cout << "No" << endl; continue; } } else { if(a == 0 && b == 1){ if(n == 1 || m == 1){ cout << "No" << endl; continue; } else{ cout << "Yes" << endl; continue; } } if(a == 0 && b == 0){ cout << "No" << endl; continue; } } cout << "Yes" << endl; } return 0; }
H. 入 (思维/图论) 题意 给定一个无向图,每一个顶点都有权重 $a_i$ ,起初将棋子放在一顶点上,每次移动中棋子都会选择相邻顶点中权重最小的,如果所有相邻顶点的权重都大于当前顶点,则立即停止。
可以自由分配各个顶点的权重(保证他们都是唯一的)并选择棋子的初始位置,求棋子能访问的顶点数的最大可能值。
数据范围
$1 \le n \le 40$
$1 \le m \le \binom{n}{2}$
$1 \le u,v \le n$
思路 题目所求的是从某点出发的链长度,但权重可以人为地制定,所以就是求最大的链长。
仔细考虑限制条件,由于选择的顶点是相邻权重最小的,显然,当选择一个节点移动后,其余节点往后必不可能选择,因为选择的权重是依次递减的。
由于题设中 $n$ 和 $m$ 的范围极小,可以枚举每个点作为起始点,移动时使用“ dfs+回溯 ”的方式依次标记和记录相邻节点以限定顶点路线,最终维护访问节点数的最大值即可 .
Code: 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> #define endl '\n' using i64 = long long; using namespace std; signed main(){ std::ios::sync_with_stdio(false);cin.tie(0); int n, m; cin >> n >> m; vector<vector<int>> e(n + 1); vector<vector<int>> g(n + 1); // 每一个点当前可走到的相邻边 vector<bool> vis(n + 1); while (m--){ int u, v;cin >> u >> v; e[u].emplace_back(v); e[v].emplace_back(u); } int ans = 0; auto dfs = [&](auto dfs, int u, int fa, int depth) -> void { ans = max(ans, depth); for (auto x:e[u]) if(x!=fa&&!vis[x]) vis[x] = 1, g[u].emplace_back(x); for(auto x:g[u]) if(x!=fa) dfs(dfs, x, u, depth + 1); for(auto x:g[u]) vis[x] = 0; g[u].clear(); }; for (int i = 1; i <= n;i++){ fill(vis.begin(), vis.end(), 0); dfs(dfs, i, 0, 1); } cout << ans << endl; return 0; }