A. Chess For Three (思维)
题意:
三个人在一起下棋。每盘棋都有两个人对弈。胜者得
2
2
2 分,负者得
0
0
0 分,如果和棋,双方各得
1
1
1 分。请注意,同一对棋手可能下过任何次数的非负数(可能为零)。也有可能根本没有下过棋。下完所有棋局后,他们的得分分别是
p
1
p_1
p1 、
p
2
p_2
p2 和
p
3
p_3
p3 。此外,可以保证
p
1
≤
p
2
≤
p
3
p_1 \leq p_2 \leq p_3
p1≤p2≤p3 成立。
找出可能发生的最大和棋次数,如果无解则输出
−
1
-1
−1 。
分析:
三个人每局对总分数的贡献都是偶数,所以最后分数总和一定是偶数。如果 a + b ≥ c a+b \ge c a+b≥c,那么可以找到一种方法平局 ( a + b + c ) / 2 (a+b+c)/2 (a+b+c)/2次。否则最多平局 a + b a+b a+b次。
代码:
#include <bits/stdc++.h>
using namespace std;
int main() {
int t;
cin >> t;
while (t--) {
int a, b, c;
cin >> a >> b >> c;
int s = a + b + c;
if (s % 2)
cout << -1 << endl;
else
cout << min(s / 2, a + b) << endl;
}
return 0;
}
B.Cat, Fox and the Lonely Array (位运算)
题意:
将 a a a 的孤度定义为:对于任意两个正整数 i i i 和 j j j ( 1 ≤ i , j ≤ n − k + 1 1 \leq i, j \leq n - k +1 1≤i,j≤n−k+1 ),以下条件成立的最小正整数 k k k ( 1 ≤ k ≤ n 1 \le k \le n 1≤k≤n ):
a i ∣ a i + 1 ∣ … ∣ a i + k − 1 = a j ∣ a j + 1 ∣ … ∣ a j + k − 1 , a_i | a_{i+1} | \ldots | a_{i+k-1} = a_j | a_{j+1} | \ldots | a_{j+k-1}, ai∣ai+1∣…∣ai+k−1=aj∣aj+1∣…∣aj+k−1,
即对于连续的 k k k个元素,它们的位相 O R OR OR 应该是相同的。求数组 a a a的孤度
分析:
我们分别对于每一位进行考虑,满足条件 k k k的最小值,为最长连续的 0 0 0的长度加 1 1 1,如果没有 1 1 1,那么这个值是可以任意取的,最后对所有的值取最大值即可。
代码:
#include <bits/stdc++.h>
using namespace std;
int main() {
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
auto cal = [&](int x) -> int {
int s = 0, flag = 0;
for (int i = 0; i < n; i++) {
if (a[i] >> x & 1) {
flag = 1;
continue;
}
int j = i + 1;
while (j < n && !(a[j] >> x & 1))
j++;
s = max(s, j - i);
i = j - 1;
}
if (!flag)
s = 0;
return s + 1;
};
int res = 1;
for (int i = 0; i < 30; i++) {
res = max(res, cal(i));
}
cout << res << endl;
}
return 0;
}
C.Cat, Fox and Double Maximum (思维)
题意:
给一个偶数
n
n
n和长度为
n
n
n 的排列
p
p
p 。要求出另外一个排列
q
q
q,使得
p
p
p与
q
q
q对应位置之和构成的数组
a
a
a,
a
a
a数组的局部最大值数量最多。
局部最大值定义为: 符合
1
≤
i
≤
n
1 \le i \le n
1≤i≤n 并且
a
i
−
1
≤
a
i
a_{i-1} \le a_i
ai−1≤ai 和
a
i
≤
a
i
+
1
a_i \le a_{i+1}
ai≤ai+1的位置
i
i
i。
分析:
分析发现最终结果一定是合法奇数位都满足或者合法偶数位都满足这两种情况。我们将最大的 n / 2 n/2 n/2个数字都放在选定的数位上,按照小的和大的匹配的原则。数位选定的方式就是哪个数位有 1 1 1就选定与其相反的数位。
代码:
#include <bits/stdc++.h>
using namespace std;
int main() {
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
vector<int> p(n + 1);
for (int i = 1; i <= n; i++)
cin >> p[i];
int pos = -1;
for (int i = 1; i <= n; i++) {
if (p[i] == 1) {
pos = i % 2;
break;
}
}
vector<int> ans(n + 1);
vector<int> tmp1, tmp2;
for (int i = 1; i <= n; i++) {
if (i % 2 == pos)
tmp2.push_back(i);
else
tmp1.push_back(i);
}
sort(tmp1.begin(), tmp1.end(), [&](int a, int b) { return p[a] < p[b]; });
sort(tmp2.begin(), tmp2.end(), [&](int a, int b) { return p[a] < p[b]; });
int num = n;
for (auto x: tmp1) {
ans[x] = num--;
}
for (auto x: tmp2) {
ans[x] = num--;
}
for (int i = 1; i <= n; i++) {
cout << ans[i] << " ";
}
cout << endl;
}
return 0;
}
D.Cat, Fox and Maximum Array Split (交互)
题意:
这是一个交互问题
给出两个正整数
n
n
n 和
k
k
k 。有一个长度为
n
n
n 的隐藏数组
a
1
,
…
,
a
n
a_1, \ldots , a_n
a1,…,an,对于任意两个整数
l
,
r
l, r
l,r 中的
1
≤
l
≤
r
≤
n
1 \leq l \leq r \leq n
1≤l≤r≤n ,定义
f
(
l
,
r
)
=
(
r
−
l
+
1
)
⋅
max
x
=
l
r
a
x
f(l, r) = (r - l + 1) \cdot \max\limits_{x=l}^r a_x
f(l,r)=(r−l+1)⋅x=lmaxrax 。换句话说,
f
(
l
,
r
)
f(l, r)
f(l,r) 等于子数组
a
l
,
…
,
a
r
a_l, \ldots, a_r
al,…,ar 的最大值乘以它的大小。
最多提出关于数组的
2
n
2 n
2n 个问题。系统会告诉你两个整数
l
l
l 和
x
x
x (
1
≤
l
≤
n
,
1
≤
x
≤
1
0
9
1 \leq l \leq n, 1 \leq x \leq 10^9
1≤l≤n,1≤x≤109 ),而你需要告诉系统一个整数
p
p
p 作为答案--
f
(
l
,
r
)
=
x
f(l, r) = x
f(l,r)=x 的最小正整数
r
r
r ,如果不存在这样的
r
r
r ,则答案为
n
+
1
n+1
n+1 。
现在你需要找到一个最大的值
m
m
m ,使得存在一个序列
c
1
,
…
,
c
k
−
1
c_1, \ldots, c_{k-1}
c1,…,ck−1 ,即
1
≤
c
1
≤
…
≤
c
k
−
1
≤
n
1 \leq c_1 \le \ldots \le c_{k-1} \le n
1≤c1≤…≤ck−1≤n 和
f
(
1
,
c
1
)
=
f
(
c
1
+
1
,
c
2
)
=
…
=
f
(
c
k
−
1
+
1
,
n
)
=
m
f(1, c_1) = f(c_1 + 1, c_2) = \ldots = f(c_{k-1}+1, n) = m
f(1,c1)=f(c1+1,c2)=…=f(ck−1+1,n)=m 。如果不存在这样的
m
m
m ,他应该指出这一点,并将
−
1
-1
−1 作为答案。注意对于
k
=
1
k = 1
k=1 ,
m
m
m 总是等于
f
(
1
,
n
)
f(1, n)
f(1,n) 。
换句话说,我们的目标是找到最大的
m
m
m ,从而可以将数组精确分割成
k
k
k 个子数组(
k
k
k 是互动开始时给定的常数),使得所有子数组的长度与最大值的乘积等于
m
m
m ,或者确定不存在这样的
m
m
m 。
分析:
我们可以先用
n
n
n次询问找出最大值,接着我们可以发现答案一定是最大值的倍数。所以我们可以直接枚举所有倍数,然后判断是否成立。
可以发现,可能的结果只有
n
/
k
n/k
n/k种,每次判断最多需要
k
k
k次,所以总次数不超过
2
×
n
2 \times n
2×n次。
代码:
#include <bits/stdc++.h>
using namespace std;
int main() {
int T;
cin >> T;
while (T--) {
int n, k;
cin >> n >> k;
int maxval = -1;
auto get = [&](int l, int x) {
cout << "? " << l << ' ' << x << endl;
int tmp;
cin >> tmp;
return tmp;
};
for (int i = 1; i <= n; i++) {
if (get(1, i * n) == n) {
maxval = i;
break;
}
}
int ans = -1;
for (int i = 1; i <= n / k; i++) {
int target = i * maxval;
int cnt = 0, st = 1;
while (st <= n) {
st = get(st, target);
if (st == n + 1) {
cnt = -1;
break;
}
st += 1;
cnt += 1;
if (cnt == k && st != n + 1) {
cnt = -1;
break;
}
}
if (cnt == k) {
ans = max(ans, target);
}
}
cout << "! " << ans << endl;
int tmp;
cin >> tmp;
}
return 0;
}
E. Cat, Fox and Swaps (图论)
题意:
有一个数组
p
1
,
p
2
,
…
,
p
n
p_1, p_2, \ldots, p_n
p1,p2,…,pn ,是长度为
n
n
n 的数字
1
,
2
,
…
,
n
1, 2, \ldots, n
1,2,…,n 的排列。现在想把这些元素按递增顺序排序。可以通过交换数组中任意两个数字
x
x
x 和
y
y
y ,但前提是
l
≤
x
+
y
≤
r
l \leq x + y \leq r
l≤x+y≤r (注意,约束条件是元素的值,而不是它们的位置)。
数字
l
l
l 、
r
r
r未给出 ,但是保证
1
≤
l
≤
r
≤
2
⋅
n
1 \leq l \leq r \leq 2 \cdot n
1≤l≤r≤2⋅n 。
给你一个数字
n
n
n 和数组
p
1
,
p
2
,
…
,
p
n
p_1, p_2, \ldots, p_n
p1,p2,…,pn 。如果你只能交换
(
x
,
y
)
(x, y)
(x,y) 这样的两个数
l
≤
x
+
y
≤
r
l \leq x + y \leq r
l≤x+y≤r 来对数组排序,计算有多少对满足条件的
(
l
,
r
)
(l,r)
(l,r)。
分析:
对于固定的 x + y x+y x+y,我们可以认为是把所有符合要求的 x , y x,y x,y之间连一条边,每个连通块内的顺序都可以任意调整。当 [ l , r ] [l,r] [l,r]区间长度大于等于 2 2 2的时候联通块一定是整个区间。这时候我们需要判断区间的前后是否符合题意,当 l l l增加时,最小的合法 r r r也是增加的,可以通过双指针求解。当 l = r l=r l=r时需要进行特判,对于一个确定的 l l l,他所影响的数也为一个区间。对于一个 i i i,如果 p i = i p_i=i pi=i对任何 l l l有效, p i ≠ i p_i \neq i pi=i只对 l = i + p i l=i+p_i l=i+pi有效。那么我们对于所有的 l l l,只需要判断当前 l l l涉及的数是否都在 l l l处符合要求。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int main() {
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];
vector<int> pre(n + 2, 1), suf(n + 2, 1);
for (int i = 1; i <= n; i++) {
pre[i] = pre[i - 1] & (a[i] == i);
}
for (int i = n; i >= 1; i--) {
suf[i] = suf[i + 1] & (a[i] == i);
}
auto cal = [&](int l, int r) {
int L = max(1, l - n), R = min(n, r - 1);
return pre[L - 1] && suf[R + 1];
};
LL ans = 0;
for (int i = 1, j = 1; i <= 2 * n; i++) {
j = max(j, i + 1);
while (j <= 2 * n && !cal(i, j))
j++;
ans += 2 * n - j + 1;
}
vector<int> s(n + 1);
vector<int> tmp(2 * n + 1);
for (int i = 1; i <= n; i++) {
s[i] = s[i - 1] + (a[i] == i);
if (a[i] != i)
tmp[a[i] + i] += 1;
}
for (int i = 1; i <= 2 * n; i++) {
int l = max(1, i - n);
int r = min(n, i - 1);
if (l > r) {
ans += pre[n];
} else if (s[r] - s[l - 1] + tmp[i] == r - l + 1) {
ans += (pre[l - 1] && suf[r + 1]);
}
}
cout << ans << endl;
}
return 0;
}
赛后交流
在比赛结束后,会在交流群中给出比赛题解,同学们可以在赛后查看题解进行补题。
群号: 704572101,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。