A. Find K Distinct Points with Fixed Center (思维)
题意:
给你三个整数 xcx_cxc 、 ycy_cyc 和 kkk ( −100≤xc,yc≤100-100 \leq x_c, y_c \leq 100−100≤xc,yc≤100 , 1≤k≤10001 \leq k \leq 10001≤k≤1000 )。
你需要在2D2D2D坐标平面上找到具有整数坐标的 kkk 个不同点 ( x1,y1x_1, y_1x1,y1 )、( x2,y2x_2, y_2x2,y2 )、 …\ldots… 、( xk,ykx_k, y_kxk,yk ),并且:
- 它们的中心为 ( xc,ycx_c, y_cxc,yc )
- 对于从 111 到 kkk 的所有 iii , −109≤xi,yi≤109-10^9 \leq x_i, y_i \leq 10^9−109≤xi,yi≤109
可以证明,至少有一组 kkk 个不同点始终存在,并且满足这些条件。
kkk 个点 ( x1,y1x_1, y_1x1,y1 )、( x2,y2x_2, y_2x2,y2 )、 …\ldots… 、( xk,ykx_k, y_kxk,yk ) 的中心是 (x1+x2+…+xkk,y1+y2+…+ykk)\left( \frac{x_1 + x_2 + \ldots + x_k}{k}, \frac{y_1 + y_2 + \ldots + y_k}{k} \right)(kx1+x2+…+xk,ky1+y2+…+yk) 。
分析:
如果需要偶数个点,那我们就输出k/2k/2k/2个相对(x,y)(x, y)(x,y)中心对称的点即可;如果需要奇数个点,再输出(x,y)(x, y)(x,y)即可。
代码:
#include <bits/stdc++.h>
using namespace std;
const int mod = 998244353;
int main() {
int t;
cin >> t;
while (t--) {
int x, y, k;
cin >> x >> y >> k;
if (k & 1) {
cout << x << " " << y << endl;
k--;
}
k /= 2;
for (int i = 1; i <= k; i++) {
cout << x + i << " " << y << endl;
cout << x - i << " " << y << endl;
}
}
return 0;
}
B. Minimize Equal Sum Subarrays (思维)
题意:
给定一个长度为 nnn 的排列 ppp 。
找到一个长度为 nnn 的排列 qqq ,使对数 ( i,ji, ji,j ) ( 1≤i≤j≤n1 \leq i \leq j \leq n1≤i≤j≤n ) 最小化,使得 pi+pi+1+…+pj=qi+qi+1+…+qjp_i + p_{i+1} + \ldots + p_j = q_i + q_{i+1} + \ldots + q_jpi+pi+1+…+pj=qi+qi+1+…+qj 。
分析:
输出排列ppp的循环左移一位或者循环右移一位的结果即可。只有[1,n][1,n][1,n]这个区间和是相等的。
代码:
#include <bits/stdc++.h>
using namespace std;
const int mod = 998244353;
int main() {
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
vector<int> a(n);
for (int &x: a)
cin >> x;
for (int i = 1; i < n; i++)
cout << a[i] << " ";
cout << a[0] << endl;
}
return 0;
}
C.Perform Operations to Maximize Score (二分)
题意:
你将获得一个长度为 nnn 的数组 aaa 和一个整数 kkk 。您还将获得一个长度为 nnn 的二进制数组 bbb 。
你最多可 以执行以下操作 kkk 次:
- 选择一个索引 iii ( 1≤i≤n1 \leq i \leq n1≤i≤n ),并且 bi=1b_i = 1bi=1 。设置 ai=ai+1a_i = a_i + 1ai=ai+1 (即,将 aia_iai 增加 111 )。
你的得分定义为 maxi=1n(ai+median(ci))\max\limits_{i = 1}^{n} \left( a_i + \operatorname{median}(c_i) \right)i=1maxn(ai+median(ci)) ,其中 cic_ici 表示从 aaa 中删除 aia_iai 后得到的长度为 n−1n-1n−1 的数组。换句话说,你的得分是从 111 到 nnn 的所有 iii 中 ai+median(ci)a_i + \operatorname{median}(c_i)ai+median(ci) 的最大值。
median(ci)\operatorname{median}(c_i)median(ci)表示cic_ici的中位数。
找出以最佳方式执行操作后可以获得的最高得分。
分析:
对于bi=1b_i=1bi=1位置,最优的操作是直接加上aia_iai。而bi=0b_i=0bi=0的位置只能通过加别的数使得中位数变大。
我们先把数字按照0/10/10/1进行分类,再考虑二分答案,判断能否从剩下的数字中找出⌈n2⌉\lceil \frac{n}{2} \rceil⌈2n⌉个大于midmidmid的数字。对于bi=0b_i=0bi=0的位置,只需要二分一下还需要提供多少个数字即可。这样我们就能知道还需要bi=1b_i=1bi=1提供多少个数字。显然我们会选择aia_iai最大的数字,再利用前缀和判断代价是否会超过kkk即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
struct Point {
LL x, y;
};
bool operator<(const Point &a, const Point &b) {
return a.x < b.x || (a.x == b.x && a.y < b.y);
}
int main() {
int t;
cin >> t;
while (t--) {
int n, k;
cin >> n >> k;
vector<Point> a(n + 1);
for (int i = 1; i <= n; i++)
cin >> a[i].x;
for (int i = 1; i <= n; i++)
cin >> a[i].y;
sort(a.begin() + 1, a.end());
LL ans = a[n / 2].x + a[n].x;
LL tmp = 0;
if (a[n].y == 1) {
cout << ans + k << endl;
continue;
}
for (int i = n; i >= 1; i--) {
if (a[i].y) {
tmp = i;
break;
}
}
LL l = a[n / 2].x, r = l + k;
while (l <= r) {
LL mid = (l + r) >> 1;
LL all = n, cost = k;
for (int i = n; i > 0 && all >= n / 2; i--) {
if (a[i].x < mid && a[i].y) {
if (a[i].x + cost >= mid) {
cost -= mid - a[i].x;
all--;
}
}
if (a[i].x >= mid) {
all--;
}
}
if (all < n / 2) {
l = mid + 1;
ans = max(ans, mid + a[n].x);
} else {
r = mid - 1;
}
}
a[tmp].x += k;
sort(a.begin() + 1, a.end());
ans = max(ans, a[n / 2].x + a[n].x);
cout << ans << endl;
}
return 0;
}
D.Determine Winning Islands in Race (dp)
题意:
农夫约翰的两头奶牛,贝西和埃尔西,计划在 nnn 个岛屿上赛跑。所有 1≤i≤n−11 \leq i \leq n - 11≤i≤n−1 岛上有 n−1n - 1n−1 座主桥,连接岛屿 iii 和岛屿 i+1i + 1i+1 。此外,还有 mmm 座备选桥。埃尔西可以使用主桥和备选桥,而贝西只能使用主桥。所有桥都是单向的,只能用于从索引较低的岛屿前往索引较高的岛屿。
最初,埃尔西从岛屿 111 出发,贝西从岛屿 sss 出发。奶牛轮流转弯,贝西先转弯。假设奶牛在岛屿 iii 上。在奶牛的回合中,如果有任何桥梁连接岛屿 iii 和岛屿 jjj ,那么奶牛可以移动到岛屿 jjj 。然后,岛屿 iii 倒塌,所有连接到岛屿 iii 的桥梁也会倒塌。另外,请注意以下几点:
- 如果没有桥梁连接岛屿 iii 和另一个岛屿,那么岛屿 iii 会倒塌,这头奶牛将被淘汰出局。
- 如果另一头奶牛也在岛屿 iii 上,那么在这头奶牛移动到另一个岛屿后,岛屿会倒塌,另一头奶牛将被淘汰出局。
- 岛屿或桥梁倒塌后,任何奶牛都不能使用它们。
- 如果一头奶牛被淘汰,则在比赛的剩余时间里,将跳过这头奶牛的回合。
一旦其中一头奶牛到达岛屿 nnn ,比赛就结束。可以证明,无论奶牛的策略如何,至少有一头奶牛会到达岛屿 nnn 。当且仅当 BessieBessieBessie 先到达岛屿 nnn 时,她才会获胜。
对于每个 1≤s≤n−11 \leq s \leq n - 11≤s≤n−1 ,确定如果 BessieBessieBessie 从岛屿 sss 开始比赛,她是否会获胜。假设两头奶牛都遵循最佳策略来确保各自的胜利。
分析:
我们用fif_ifi表示ElsieElsieElsie到iii的最少步数,那么当ElsieElsieElsie 到iii且打算移动时,BessieBessieBessie 已经移动到S+fi+1S+f_i+1S+fi+1,那么ElsieElsieElsie移动到jjj,一定有j>S+f(i)+1j >S+f(i)+1j>S+f(i)+1才能赢,移项后得到BessieBessieBessie赢的条件是S≥j−f(i)−1S \ge j-f(i)-1S≥j−f(i)−1,而不等号右边的式子在枚举SSS时不断维护最大值即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int main() {
int t;
cin >> t;
while (t--) {
int n, m;
cin >> n >> m;
vector<LL> a(n + 1);
vector<vector<LL>> g(n + 1);
for (int i = 0; i < m; i++) {
LL u, v;
cin >> u >> v;
a[u] = max(a[u], v);
g[v].push_back(u);
}
vector<LL> f(n + 1, n);
f[1] = 0;
LL maxval = 0;
for (int i = 1; i < n; i++) {
if (i >= maxval)
cout << 1;
else
cout << 0;
f[i] = min(f[i], f[i - 1] + 1);
for (auto j: g[i]) {
f[i] = min(f[i], f[j] + 1);
}
maxval = max(maxval, a[i] - f[i] - 1);
}
cout << endl;
}
return 0;
}
E1.Eliminating Balls With Merging (Easy Version) (二分)
题意:
你有两个整数 nnn 和 xxx ( x=nx=nx=n )。有 nnn 个球排成一排,从左到右编号为 111 到 nnn 。最初,在第 iii 个球上写有一个值 aia_iai 。
对于从 111 到 nnn 的每个整数 iii ,我们定义一个函数 f(i)f(i)f(i) ,如下所示:
-
假设你有一个集合 S={1,2,…,i}S = \{1, 2, \ldots, i\}S={1,2,…,i} 。
-
在每个操作中,您必须从 SSS 中选择一个整数 lll ( 1≤l<i1 \leq l < i1≤l<i ),使得 lll 不是 SSS 中的最大元素。假设 rrr 是 SSS 中大于 lll 的最小元素。
-
如果是 al>ara_l > a_ral>ar ,则设置 al=al+ara_l = a_l + a_ral=al+ar 并从 SSS 中删除 rrr 。
-
如果是 al<ara_l < a_ral<ar ,则设置 ar=al+ara_r = a_l + a_rar=al+ar 并从 SSS 中删除 lll 。
-
如果是 al=ara_l = a_ral=ar ,则选择从 SSS 中删除整数 lll 或 rrr :
-
如果选择从 SSS 中删除 lll ,则设置 ar=al+ara_r = a_l + a_rar=al+ar 并从 SSS 中删除 lll 。
-
如果选择从 SSS 中删除 rrr ,则设置 al=al+ara_l = a_l + a_ral=al+ar 并从 SSS 中删除 rrr 。
-
f(i)f(i)f(i) 表示整数 jjj ( 1≤j≤i1 \le j \le i1≤j≤i ) 的数量,这样在执行上述操作恰好 i−1i - 1i−1 次后,可以获得 S={j}S = \{j\}S={j} 。
对于从 xxx 到 nnn 的每个整数 iii ,你需要找到 f(i)f(i)f(i) 。
分析:
我们发现最优操作肯定是从位置iii开始,对于左右,能吃掉就尽量吃掉,再判断能否吃掉所有数字。如果一个一个吃的话,那每次模拟复杂度为O(n)O(n)O(n),不可以接受。我们假设某个时刻已经吃掉的范围为[l,r][l,r][l,r]。在这个过程中,我们可以每次向左向右找到第一个大于当前值的数字,然后判断能否能否吃掉这个数字。可以发现,每次多吃一个数字后当前值至少乘222,这个过程最多进行logloglog次。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
const int mod = 998244353;
int n, E, a[N], ls[N], rs[N], top, tmp[N];
LL s1[N], s2[N];
void dfs(int x) {
if (!x)
return;
dfs(ls[x]), dfs(rs[x]);
s1[x] = s1[ls[x]] + s1[rs[x]] + a[x];
s2[x] = 1;
if (s1[ls[x]] >= a[x])
s2[x] += s2[ls[x]];
if (s1[rs[x]] >= a[x])
s2[x] += s2[rs[x]];
}
int main() {
int t;
cin >> t;
while (t--) {
top = 0;
cin >> n >> E;
for (int i = 1; i <= n; i++) {
ls[i] = rs[i] = 0;
cin >> a[i];
while (top && a[tmp[top]] <= a[i])
ls[i] = tmp[top],
top--;
if (top)
rs[tmp[top]] = i;
tmp[++top] = i;
}
dfs(tmp[1]);
cout << s2[tmp[1]] << endl;
}
return 0;
}1
赛后交流
在比赛结束后,会在交流群中给出比赛题解,同学们可以在赛后查看题解进行补题。
群号: 704572101,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。

1289





