A. Find K Distinct Points with Fixed Center (思维)
题意:
给你三个整数 x c x_c xc 、 y c y_c yc 和 k k k ( − 100 ≤ x c , y c ≤ 100 -100 \leq x_c, y_c \leq 100 −100≤xc,yc≤100 , 1 ≤ k ≤ 1000 1 \leq k \leq 1000 1≤k≤1000 )。
你需要在 2 D 2D 2D坐标平面上找到具有整数坐标的 k k k 个不同点 ( x 1 , y 1 x_1, y_1 x1,y1 )、( x 2 , y 2 x_2, y_2 x2,y2 )、 … \ldots … 、( x k , y k x_k, y_k xk,yk ),并且:
- 它们的中心为 ( x c , y c x_c, y_c xc,yc )
- 对于从 1 1 1 到 k k k 的所有 i i i , − 1 0 9 ≤ x i , y i ≤ 1 0 9 -10^9 \leq x_i, y_i \leq 10^9 −109≤xi,yi≤109
可以证明,至少有一组 k k k 个不同点始终存在,并且满足这些条件。
k k k 个点 ( x 1 , y 1 x_1, y_1 x1,y1 )、( x 2 , y 2 x_2, y_2 x2,y2 )、 … \ldots … 、( x k , y k x_k, y_k xk,yk ) 的中心是 ( x 1 + x 2 + … + x k k , y 1 + y 2 + … + y k k ) \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 / 2 k/2 k/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 (思维)
题意:
给定一个长度为 n n n 的排列 p p p 。
找到一个长度为 n n n 的排列 q q q ,使对数 ( i , j i, j i,j ) ( 1 ≤ i ≤ j ≤ n 1 \leq i \leq j \leq n 1≤i≤j≤n ) 最小化,使得 p i + p i + 1 + … + p j = q i + q i + 1 + … + q j p_i + p_{i+1} + \ldots + p_j = q_i + q_{i+1} + \ldots + q_j pi+pi+1+…+pj=qi+qi+1+…+qj 。
分析:
输出排列 p p p的循环左移一位或者循环右移一位的结果即可。只有 [ 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 (二分)
题意:
你将获得一个长度为 n n n 的数组 a a a 和一个整数 k k k 。您还将获得一个长度为 n n n 的二进制数组 b b b 。
你最多可 以执行以下操作 k k k 次:
- 选择一个索引 i i i ( 1 ≤ i ≤ n 1 \leq i \leq n 1≤i≤n ),并且 b i = 1 b_i = 1 bi=1 。设置 a i = a i + 1 a_i = a_i + 1 ai=ai+1 (即,将 a i a_i ai 增加 1 1 1 )。
你的得分定义为
max
i
=
1
n
(
a
i
+
median
(
c
i
)
)
\max\limits_{i = 1}^{n} \left( a_i + \operatorname{median}(c_i) \right)
i=1maxn(ai+median(ci)) ,其中
c
i
c_i
ci 表示从
a
a
a 中删除
a
i
a_i
ai 后得到的长度为
n
−
1
n-1
n−1 的数组。换句话说,你的得分是从
1
1
1 到
n
n
n 的所有
i
i
i 中
a
i
+
median
(
c
i
)
a_i + \operatorname{median}(c_i)
ai+median(ci) 的最大值。
median
(
c
i
)
\operatorname{median}(c_i)
median(ci)表示
c
i
c_i
ci的中位数。
找出以最佳方式执行操作后可以获得的最高得分。
分析:
对于
b
i
=
1
b_i=1
bi=1位置,最优的操作是直接加上
a
i
a_i
ai。而
b
i
=
0
b_i=0
bi=0的位置只能通过加别的数使得中位数变大。
我们先把数字按照
0
/
1
0/1
0/1进行分类,再考虑二分答案,判断能否从剩下的数字中找出
⌈
n
2
⌉
\lceil \frac{n}{2} \rceil
⌈2n⌉个大于
m
i
d
mid
mid的数字。对于
b
i
=
0
b_i=0
bi=0的位置,只需要二分一下还需要提供多少个数字即可。这样我们就能知道还需要
b
i
=
1
b_i=1
bi=1提供多少个数字。显然我们会选择
a
i
a_i
ai最大的数字,再利用前缀和判断代价是否会超过
k
k
k即可。
代码:
#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)
题意:
农夫约翰的两头奶牛,贝西和埃尔西,计划在 n n n 个岛屿上赛跑。所有 1 ≤ i ≤ n − 1 1 \leq i \leq n - 1 1≤i≤n−1 岛上有 n − 1 n - 1 n−1 座主桥,连接岛屿 i i i 和岛屿 i + 1 i + 1 i+1 。此外,还有 m m m 座备选桥。埃尔西可以使用主桥和备选桥,而贝西只能使用主桥。所有桥都是单向的,只能用于从索引较低的岛屿前往索引较高的岛屿。
最初,埃尔西从岛屿 1 1 1 出发,贝西从岛屿 s s s 出发。奶牛轮流转弯,贝西先转弯。假设奶牛在岛屿 i i i 上。在奶牛的回合中,如果有任何桥梁连接岛屿 i i i 和岛屿 j j j ,那么奶牛可以移动到岛屿 j j j 。然后,岛屿 i i i 倒塌,所有连接到岛屿 i i i 的桥梁也会倒塌。另外,请注意以下几点:
- 如果没有桥梁连接岛屿 i i i 和另一个岛屿,那么岛屿 i i i 会倒塌,这头奶牛将被淘汰出局。
- 如果另一头奶牛也在岛屿 i i i 上,那么在这头奶牛移动到另一个岛屿后,岛屿会倒塌,另一头奶牛将被淘汰出局。
- 岛屿或桥梁倒塌后,任何奶牛都不能使用它们。
- 如果一头奶牛被淘汰,则在比赛的剩余时间里,将跳过这头奶牛的回合。
一旦其中一头奶牛到达岛屿 n n n ,比赛就结束。可以证明,无论奶牛的策略如何,至少有一头奶牛会到达岛屿 n n n 。当且仅当 B e s s i e Bessie Bessie 先到达岛屿 n n n 时,她才会获胜。
对于每个 1 ≤ s ≤ n − 1 1 \leq s \leq n - 1 1≤s≤n−1 ,确定如果 B e s s i e Bessie Bessie 从岛屿 s s s 开始比赛,她是否会获胜。假设两头奶牛都遵循最佳策略来确保各自的胜利。
分析:
我们用 f i f_i fi表示 E l s i e Elsie Elsie到 i i i的最少步数,那么当 E l s i e Elsie Elsie 到 i i i且打算移动时, B e s s i e Bessie Bessie 已经移动到 S + f i + 1 S+f_i+1 S+fi+1,那么 E l s i e Elsie Elsie移动到 j j j,一定有 j > S + f ( i ) + 1 j >S+f(i)+1 j>S+f(i)+1才能赢,移项后得到 B e s s i e Bessie Bessie赢的条件是 S ≥ j − f ( i ) − 1 S \ge j-f(i)-1 S≥j−f(i)−1,而不等号右边的式子在枚举 S S S时不断维护最大值即可。
代码:
#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) (二分)
题意:
你有两个整数 n n n 和 x x x ( x = n x=n x=n )。有 n n n 个球排成一排,从左到右编号为 1 1 1 到 n n n 。最初,在第 i i i 个球上写有一个值 a i a_i ai 。
对于从 1 1 1 到 n n n 的每个整数 i i i ,我们定义一个函数 f ( i ) f(i) f(i) ,如下所示:
-
假设你有一个集合 S = { 1 , 2 , … , i } S = \{1, 2, \ldots, i\} S={1,2,…,i} 。
-
在每个操作中,您必须从 S S S 中选择一个整数 l l l ( 1 ≤ l < i 1 \leq l < i 1≤l<i ),使得 l l l 不是 S S S 中的最大元素。假设 r r r 是 S S S 中大于 l l l 的最小元素。
-
如果是 a l > a r a_l > a_r al>ar ,则设置 a l = a l + a r a_l = a_l + a_r al=al+ar 并从 S S S 中删除 r r r 。
-
如果是 a l < a r a_l < a_r al<ar ,则设置 a r = a l + a r a_r = a_l + a_r ar=al+ar 并从 S S S 中删除 l l l 。
-
如果是 a l = a r a_l = a_r al=ar ,则选择从 S S S 中删除整数 l l l 或 r r r :
-
如果选择从 S S S 中删除 l l l ,则设置 a r = a l + a r a_r = a_l + a_r ar=al+ar 并从 S S S 中删除 l l l 。
-
如果选择从 S S S 中删除 r r r ,则设置 a l = a l + a r a_l = a_l + a_r al=al+ar 并从 S S S 中删除 r r r 。
-
f ( i ) f(i) f(i) 表示整数 j j j ( 1 ≤ j ≤ i 1 \le j \le i 1≤j≤i ) 的数量,这样在执行上述操作恰好 i − 1 i - 1 i−1 次后,可以获得 S = { j } S = \{j\} S={j} 。
对于从 x x x 到 n n n 的每个整数 i i i ,你需要找到 f ( i ) f(i) f(i) 。
分析:
我们发现最优操作肯定是从位置 i i i开始,对于左右,能吃掉就尽量吃掉,再判断能否吃掉所有数字。如果一个一个吃的话,那每次模拟复杂度为 O ( n ) O(n) O(n),不可以接受。我们假设某个时刻已经吃掉的范围为 [ l , r ] [l,r] [l,r]。在这个过程中,我们可以每次向左向右找到第一个大于当前值的数字,然后判断能否能否吃掉这个数字。可以发现,每次多吃一个数字后当前值至少乘 2 2 2,这个过程最多进行 l o g log log次。
代码:
#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,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。