A.Sort Left and Right(思维)
题意:
给你一个 (1,2,…,N)(1,2,\dots,N)(1,2,…,N) 的排列组合 P=(P1,P2,…,PN)P=(P_1,P_2,\dots,P_N)P=(P1,P2,…,PN) 。
你想通过执行下面的操作零次或多次来满足所有 i=1,2,…,Ni=1,2,\dots,Ni=1,2,…,N 的 Pi=iP_i=iPi=i 要求:
- 选择一个整数 kkk ,使得 1≤k≤N1 \leq k \leq N1≤k≤N .如果是 k≥2k \geq 2k≥2 ,则把 PPP 的 111 项到 (k−1)(k-1)(k−1) 项按升序排序。然后,如果是 k≤N−1k \leq N-1k≤N−1 ,把 PPP 的 (k+1)(k+1)(k+1) 项到 NNN 项按升序排序。
可以证明,在这个问题的约束条件下,对于所有的 i=1,2,…,Ni=1,2,\dots,Ni=1,2,…,N ,对于任意的 PPP ,都可以用有限次的运算满足 Pi=iP_i=iPi=i 。请求解所需的最小运算次数。
分析:
分类讨论:
- 若排列已经是升序:操作次数为000。
- 若[1,i][1,i][1,i]已经是1−i1-i1−i的排列,操作次数为111。
- 若P1≠nP_1 \neq nP1=n或者Pn≠1P_n \neq 1Pn=1操作次数为222。
- 剩余情况操作次数为333。
代码:
#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(数学)
题意:
对于由小写英文字母组成的字符串 SSS 和 TTT 以及由 0 和 1 组成的字符串 XXX ,请定义由小写英文字母组成的字符串 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∣ ,如果 XXX 的第 iii 个字符是
0,则将 SSS 追加到尾部,如果是1,则将 TTT 追加到尾部。
给你一个由小写英文字母组成的字符串 SSS 以及由0和1组成的字符串 XXX 和 YYY 。
请判断是否存在一个字符串 TTT (可以为空),使得 f(S,T,X)=f(S,T,Y)f(S,T,X)=f(S,T,Y)f(S,T,X)=f(S,T,Y) .
分析:
首先,考虑 XXX 和 YYY 中1 的个数相等的情况。如果 0的个数也相等,那么设置 T=ST=ST=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) 的长度不可能相等
如果 XXX 和 YYY 中的1 的个数不相等,则有 X≠YX\neq YX=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≠YX\neq YX=Y
f(S,T,X)=f(S,T,Y) ⟺ Sf(S,T,X)=f(S,T,Y) \iff Sf(S,T,X)=f(S,T,Y)⟺S 和 TTT 是长度为 gcd(∣S∣,∣T∣)\mathrm{gcd}(|S|,|T|)gcd(∣S∣,∣T∣) 的字符串 UUU 的重复。
因此,如果 SSS 的周期为 gcd(∣S∣,∣T∣)\mathrm{gcd}(|S|,|T|)gcd(∣S∣,∣T∣) ,就存在满足条件的 TTT 。这可以通过验证 SSS 的 iii 个字符是否等于 (i+gcd(∣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=(P1,P2,…,PN)P=(P_1,P_2,\dots,P_N)P=(P1,P2,…,PN) 和 Q=(Q1,Q2,…,QN)Q=(Q_1,Q_2,\dots,Q_N)Q=(Q1,Q2,…,QN) 。
请在 NNN 乘以 NNN 的网格中的每个单元格中写入 0和 1中的一个字符,以便满足以下所有条件:
- 假设 SiS_iSi 是将 iii / 111 行至 NNN / NNN 列中的字符连接起来得到的字符串。然后, SP1<SP2<⋯<SPNS_{P_1} < S_{P_2} < \dots < S_{P_N}SP1<SP2<⋯<SPN 按词典顺序排列。
- 设 TiT_iTi 是将 iii列中的字符从 111 连接到 NNN 行得到的字符串。然后, TQ1<TQ2<⋯<TQNT_{Q_1} < T_{Q_2} < \dots < T_{Q_N}TQ1<TQ2<⋯<TQN 按词典顺序排列。
可以证明,对于任意 PPP 和 QQQ ,至少有一种写法满足所有条件。
分析:
在第 P1P_1P1 行的所有单元格中写入 0,在第 QNQ_NQN 列中所有仍为空的单元格中写入 1,对于剩余的 (N−1)×(N−1)(N-1) \times (N-1)(N−1)×(N−1) 单元格,假设 Si′S'_iSi′ 是将第 iii 行连接到不包括第 QNQ_NQN 列所得到的字符串,而 Tj′T'_jTj′ 是将第 jjj 列连接到不包括第 P1P_1P1 行所得到的字符串。然后,用同样的方法递归写出 SP2′<SP3′<⋯<SPN′S'_{P_2} < S'_{P_3} < \dots < S'_{P_N}SP2′<SP3′<⋯<SPN′ 和 TQ2′<TQ3′<⋯<TQN′T'_{Q_2} < T'_{Q_3} < \dots < T'_{Q_N}TQ2′<TQ3′<⋯<TQN′ 。
通过这种算法填充的网格将满足上述条件。除了问题中所述的条件外,它还满足条件:
- 每列中至少有一个单元格为
0。
这可以通过以下对 NNN 的归纳来证明:
- 由于
0总是写在第 P1P_1P1 行,因此每列必须至少包含一个写有0的单元格。 - 第 P1P_1P1 行不包含
1,而第 P2P_2P2 行包含1,所以 SP1<SP2S_{P_1} < S_{P_2}SP1<SP2 总是成立。 - 除了第 P1P_1P1 行之外,第 QNQ_NQN 列包含
1,而第 QN−1Q_{N-1}QN−1 列除了第 P1P_1P1 行之外,包含递归写有0的单元格,因此 TQN−1<TQNT_{Q_{N-1}} < T_{Q_N}TQN−1<TQN 恒成立。 - SPi (2≤i)S_{P_i}\ (2\leq i)SPi (2≤i) 的词序等于 SPi′S'_{P_i}SPi′ 的词序,因此 SP2<SP3<⋯<SPNS_{P_2} < S_{P_3} < \dots < S_{P_N}SP2<SP3<⋯<SPN 符合递归写法。这同样适用于 TQi (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=(P1,P2,…,PN)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) 。
- 操作 kkk :对于 i=1,2,…,k−1i=1,2,\dots,k-1i=1,2,…,k−1 这个顺序,如果是 Pi>Pi+1P_i > P_{i+1}Pi>Pi+1 ,交换 PPP 的 iii 和 (i+1)(i+1)(i+1) 元素的值。
我们还给出了一个长度为 MMM 的非递减序列 A=(A1,A2,…,AM) (2≤Ai≤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,…,Mi=1,2,\dots,Mi=1,2,…,M 按此顺序进行 A1,A2,…,AiA_1, A_2, \dots, A_iA1,A2,…,Ai 运算后,求 PPP 的逆序对数。
分析:
我们可以发现每次交换操作过后序列的逆序对数减一。并且由于序列PPP单调非减,若操作kkk使xxx左边与它组成逆序对的数的数量减一,那么之后的每次操作的逆序对数同样也会减一。直到减到零,因此可以考虑使用树状数组维护逆序对,使用二分加差分维护操作对逆序对个数的影响。
代码:
#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,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。

501

被折叠的 条评论
为什么被折叠?



