A.Sort Left and Right(思维)
题意:
给你一个 ( 1 , 2 , … , N ) (1,2,\dots,N) (1,2,…,N) 的排列组合 P = ( P 1 , P 2 , … , P N ) P=(P_1,P_2,\dots,P_N) P=(P1,P2,…,PN) 。
你想通过执行下面的操作零次或多次来满足所有 i = 1 , 2 , … , N i=1,2,\dots,N i=1,2,…,N 的 P i = i P_i=i Pi=i 要求:
- 选择一个整数 k k k ,使得 1 ≤ k ≤ N 1 \leq k \leq N 1≤k≤N .如果是 k ≥ 2 k \geq 2 k≥2 ,则把 P P P 的 1 1 1 项到 ( k − 1 ) (k-1) (k−1) 项按升序排序。然后,如果是 k ≤ N − 1 k \leq N-1 k≤N−1 ,把 P P P 的 ( k + 1 ) (k+1) (k+1) 项到 N N N 项按升序排序。
可以证明,在这个问题的约束条件下,对于所有的 i = 1 , 2 , … , N i=1,2,\dots,N i=1,2,…,N ,对于任意的 P P P ,都可以用有限次的运算满足 P i = i P_i=i Pi=i 。请求解所需的最小运算次数。
分析:
分类讨论:
- 若排列已经是升序:操作次数为 0 0 0。
- 若 [ 1 , i ] [1,i] [1,i]已经是 1 − i 1-i 1−i的排列,操作次数为 1 1 1。
- 若 P 1 ≠ n P_1 \neq n P1=n或者 P n ≠ 1 P_n \neq 1 Pn=1操作次数为 2 2 2。
- 剩余情况操作次数为 3 3 3。
代码:
#include <bits/stdc++.h>
using namespace std;
void solve() {
int n;
cin >> n;
vector<int> a(n + 1);
int f = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
if (a[i] != i) {
f = 1;
}
}
if (!f) {
cout << 0 << endl;
return;
}
int mx = 0;
for (int i = 1; i <= n; i++) {
if (a[i] == i) {
if (mx == i - 1) {
cout << 1 << endl;
return;
}
}
mx = max(a[i], mx);
}
if ((a[n] != 1) || (a[1] != n))
cout << 2 << endl;
else
cout << 3 << endl;
}
int main() {
int t = 1;
cin >> t;
while (t--) {
solve();
}
return 0;
}
B.Annoying String Problem(数学)
题意:
对于由小写英文字母组成的字符串
S
S
S 和
T
T
T 以及由 0
和 1
组成的字符串
X
X
X ,请定义由小写英文字母组成的字符串
f
(
S
,
T
,
X
)
f(S,T,X)
f(S,T,X) 如下:
- 从空字符串开始,对于每个
i
=
1
,
2
,
…
,
∣
X
∣
i=1,2,\dots,|X|
i=1,2,…,∣X∣ ,如果
X
X
X 的第
i
i
i 个字符是
0
,则将 S S S 追加到尾部,如果是1
,则将 T T T 追加到尾部。
给你一个由小写英文字母组成的字符串
S
S
S 以及由0
和1
组成的字符串
X
X
X 和
Y
Y
Y 。
请判断是否存在一个字符串 T T T (可以为空),使得 f ( S , T , X ) = f ( S , T , Y ) f(S,T,X)=f(S,T,Y) f(S,T,X)=f(S,T,Y) .
分析:
首先,考虑
X
X
X 和
Y
Y
Y 中1
的个数相等的情况。如果 0
的个数也相等,那么设置
T
=
S
T=S
T=S 显然满足
f
(
S
,
T
,
X
)
=
f
(
S
,
T
,
Y
)
f(S,T,X)=f(S,T,Y)
f(S,T,X)=f(S,T,Y) 。否则,
f
(
S
,
T
,
X
)
f(S,T,X)
f(S,T,X) 和
f
(
S
,
T
,
Y
)
f(S,T,Y)
f(S,T,Y) 的长度不可能相等
如果
X
X
X 和
Y
Y
Y 中的1
的个数不相等,则有
X
≠
Y
X\neq Y
X=Y ,而
∣
f
(
S
,
T
,
X
)
∣
=
∣
f
(
S
,
T
,
Y
)
∣
|f(S,T,X)|=|f(S,T,Y)|
∣f(S,T,X)∣=∣f(S,T,Y)∣ 可以得到一个以
∣
T
∣
|T|
∣T∣ 为单位的线性方程,从而求出
∣
T
∣
|T|
∣T∣ (如果不是整数或负数,则无解)。
根据上述观察,当
X
≠
Y
X\neq Y
X=Y
f
(
S
,
T
,
X
)
=
f
(
S
,
T
,
Y
)
⟺
S
f(S,T,X)=f(S,T,Y) \iff S
f(S,T,X)=f(S,T,Y)⟺S 和
T
T
T 是长度为
g
c
d
(
∣
S
∣
,
∣
T
∣
)
\mathrm{gcd}(|S|,|T|)
gcd(∣S∣,∣T∣) 的字符串
U
U
U 的重复。
因此,如果
S
S
S 的周期为
g
c
d
(
∣
S
∣
,
∣
T
∣
)
\mathrm{gcd}(|S|,|T|)
gcd(∣S∣,∣T∣) ,就存在满足条件的
T
T
T 。这可以通过验证
S
S
S 的
i
i
i 个字符是否等于
(
i
+
g
c
d
(
∣
S
∣
,
∣
T
∣
)
)
(i+\mathrm{gcd}(|S|,|T|))
(i+gcd(∣S∣,∣T∣)) 个字符,在
O
(
∣
S
∣
)
O(|S|)
O(∣S∣) 时间内检查出来。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int n;
string s, x, y;
LL gcd(LL a, LL b) {
return b ? gcd(b, a % b) : a;
}
int solve() {
cin >> s >> x >> y;
n = s.size();
LL cnt[2] = {};
for (char c: x)
cnt[c - '0']++;
for (char c: y)
cnt[c - '0']--;
if (!cnt[0])
return 1;
if (!cnt[1])
return 0;
if (cnt[0] * cnt[1] > 0)
return 0;
cnt[0] = abs(cnt[0]), cnt[1] = abs(cnt[1]);
if (cnt[0] * n % cnt[1])
return 0;
int g = gcd(n, cnt[0] * n / cnt[1]);
for (int i = 0; i + g < n; i++)
if (s[i + g] != s[i])
return 0;
return 1;
}
int main() {
int t = 1;
cin >> t;
while (t--) {
if (solve())
cout << "Yes" << endl;
else
cout << "No" << endl;
}
return 0;
}
C.Row and Column Order(思维)
题意:
给你 ( 1 , 2 , … , N ) (1,2,\dots,N) (1,2,…,N) 的两个排列 P = ( P 1 , P 2 , … , P N ) P=(P_1,P_2,\dots,P_N) P=(P1,P2,…,PN) 和 Q = ( Q 1 , Q 2 , … , Q N ) Q=(Q_1,Q_2,\dots,Q_N) Q=(Q1,Q2,…,QN) 。
请在
N
N
N 乘以
N
N
N 的网格中的每个单元格中写入 0
和 1
中的一个字符,以便满足以下所有条件:
- 假设 S i S_i Si 是将 i i i / 1 1 1 行至 N N N / N N N 列中的字符连接起来得到的字符串。然后, S P 1 < S P 2 < ⋯ < S P N S_{P_1} < S_{P_2} < \dots < S_{P_N} SP1<SP2<⋯<SPN 按词典顺序排列。
- 设 T i T_i Ti 是将 i i i列中的字符从 1 1 1 连接到 N N N 行得到的字符串。然后, T Q 1 < T Q 2 < ⋯ < T Q N T_{Q_1} < T_{Q_2} < \dots < T_{Q_N} TQ1<TQ2<⋯<TQN 按词典顺序排列。
可以证明,对于任意 P P P 和 Q Q Q ,至少有一种写法满足所有条件。
分析:
在第
P
1
P_1
P1 行的所有单元格中写入 0
,在第
Q
N
Q_N
QN 列中所有仍为空的单元格中写入 1
,对于剩余的
(
N
−
1
)
×
(
N
−
1
)
(N-1) \times (N-1)
(N−1)×(N−1) 单元格,假设
S
i
′
S'_i
Si′ 是将第
i
i
i 行连接到不包括第
Q
N
Q_N
QN 列所得到的字符串,而
T
j
′
T'_j
Tj′ 是将第
j
j
j 列连接到不包括第
P
1
P_1
P1 行所得到的字符串。然后,用同样的方法递归写出
S
P
2
′
<
S
P
3
′
<
⋯
<
S
P
N
′
S'_{P_2} < S'_{P_3} < \dots < S'_{P_N}
SP2′<SP3′<⋯<SPN′ 和
T
Q
2
′
<
T
Q
3
′
<
⋯
<
T
Q
N
′
T'_{Q_2} < T'_{Q_3} < \dots < T'_{Q_N}
TQ2′<TQ3′<⋯<TQN′ 。
通过这种算法填充的网格将满足上述条件。除了问题中所述的条件外,它还满足条件:
- 每列中至少有一个单元格为
0
。
这可以通过以下对 N N N 的归纳来证明:
- 由于
0
总是写在第 P 1 P_1 P1 行,因此每列必须至少包含一个写有0
的单元格。 - 第
P
1
P_1
P1 行不包含
1
,而第 P 2 P_2 P2 行包含1
,所以 S P 1 < S P 2 S_{P_1} < S_{P_2} SP1<SP2 总是成立。 - 除了第
P
1
P_1
P1 行之外,第
Q
N
Q_N
QN 列包含
1
,而第 Q N − 1 Q_{N-1} QN−1 列除了第 P 1 P_1 P1 行之外,包含递归写有0
的单元格,因此 T Q N − 1 < T Q N T_{Q_{N-1}} < T_{Q_N} TQN−1<TQN 恒成立。 - S P i ( 2 ≤ i ) S_{P_i}\ (2\leq i) SPi (2≤i) 的词序等于 S P i ′ S'_{P_i} SPi′ 的词序,因此 S P 2 < S P 3 < ⋯ < S P N S_{P_2} < S_{P_3} < \dots < S_{P_N} SP2<SP3<⋯<SPN 符合递归写法。这同样适用于 T Q i ( i ≤ N − 1 ) T_{Q_i}\ (i \leq N-1) TQi (i≤N−1) 。
代码:
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
vector<int> p(n), q(n), tmp1(n), tmp2(n);
for (auto &e: p)
cin >> e;
for (auto &e: q)
cin >> e;
for (int i = 0; i < n; i++) {
tmp1[p[i] - 1] = i + 1;
tmp2[q[i] - 1] = i + 1;
}
vector<vector<int>> a(n, vector<int>(n));
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (tmp1[i] + tmp2[j] > n)
a[i][j] = 1;
}
}
for (auto &e: a) {
for (auto &ans: e)
cout << ans;
cout << endl;
}
return 0;
}
D.Prefix Bubble Sort (数据结构)
题意:
给你一个
(
1
,
2
,
…
,
N
)
(1,2,\dots,N)
(1,2,…,N) 的排列组合
P
=
(
P
1
,
P
2
,
…
,
P
N
)
P=(P_1,P_2,\dots,P_N)
P=(P1,P2,…,PN) 。
请考虑对这个排列进行以下运算
k
(
k
=
2
,
3
,
…
,
N
)
k\ (k=2,3,\dots,N)
k (k=2,3,…,N) 。
- 操作 k k k :对于 i = 1 , 2 , … , k − 1 i=1,2,\dots,k-1 i=1,2,…,k−1 这个顺序,如果是 P i > P i + 1 P_i > P_{i+1} Pi>Pi+1 ,交换 P P P 的 i i i 和 ( i + 1 ) (i+1) (i+1) 元素的值。
我们还给出了一个长度为 M M M 的非递减序列 A = ( A 1 , A 2 , … , A M ) ( 2 ≤ A i ≤ N ) A=(A_1,A_2,\dots,A_M)\ (2 \leq A_i \leq N) A=(A1,A2,…,AM) (2≤Ai≤N) 。对每个 i = 1 , 2 , … , M i=1,2,\dots,M i=1,2,…,M 按此顺序进行 A 1 , A 2 , … , A i A_1, A_2, \dots, A_i A1,A2,…,Ai 运算后,求 P P P 的逆序对数。
分析:
我们可以发现每次交换操作过后序列的逆序对数减一。并且由于序列 P P P单调非减,若操作 k k k使 x x x左边与它组成逆序对的数的数量减一,那么之后的每次操作的逆序对数同样也会减一。直到减到零,因此可以考虑使用树状数组维护逆序对,使用二分加差分维护操作对逆序对个数的影响。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
LL tr[N], sz, num[N], ans[N];
int lowbit(int x) { return x & -x; }
void change(int x, LL d) {
while (x <= sz)
tr[x] += d, x += lowbit(x);
}
LL query(int x) {
LL res = 0;
while (x)
res += tr[x], x -= lowbit(x);
return res;
}
int main() {
int n;
cin >> n;
sz = n;
vector<int> p(n);
for (auto &x: p)
cin >> x;
LL tot = 0;
for (int i = 0; i < n; ++i) {
tot += num[i] = i - query(p[i]);
change(p[i], 1);
}
int m;
cin >> m;
vector<int> a(m);
for (auto &x: a)
cin >> x;
for (int i = 0; i < n; ++i) {
int l = lower_bound(a.begin(), a.end(), i + 1) - a.begin();
int r = min(l + num[i] - 1, (LL) m - 1);
ans[l]++, ans[r + 1]--;
}
for (int i = 1; i < m; ++i)
ans[i] += ans[i - 1];
for (int i = 1; i < m; ++i)
ans[i] += ans[i - 1];
for (int i = 0; i < m; ++i)
cout << tot - ans[i] << endl;
return 0;
}
赛后交流
在比赛结束后,会在交流群中给出比赛题解,同学们可以在赛后查看题解进行补题。
群号: 704572101,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。