题目:[[2024.10.9 题目]]
## 矩阵交换
有 $n\times m$ 的整数矩阵 $a$,每个元素均为 $1,2,3$。可以选择任意两不相等的行,将它们交换,使得每一列 $i$ 均单调不减 $a_{j,i}\le a_{j+1,i}$ $(1\le j<n)$。
---
我们对于每一个行,使用第一个不相等的列上的数为关键字从小到大进行排序,排序完之后每一行与下一行第一个不相等的列肯定是小于等于的(废话)。此时我们对于每一列进行清扫,如果发现不满足条件那也无可厚非,因为每一行都已经锁死了,直接输出无解。如果没有这种情况,输出有解即可。
```cpp
#include <fstream>
#include <algorithm>
using namespace std;
const int kMaxN = 105;
ifstream cin("exchange.in");
ofstream cout("exchange.out");
int t, n, m;
struct Line {
int a[kMaxN];
int &operator[](int p) {
return a[p];
}
friend bool operator<(const Line &a, const Line &b) {
for (int i = 1; i <= m; i++) {
if (a.a[i] != b.a[i]) {
return a.a[i] < b.a[i];
}
}
return 1;
}
} a[kMaxN];
int main() {
for (cin >> t; t; t--) {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> a[i][j];
}
}
sort(a + 1, a + n + 1);
bool b = 1;
for (int i = 1; i <= m && b; i++) {
for (int j = 2; j <= n && b; j++) {
if (a[j - 1][i] > a[j][i]) {
b = 0;
}
}
}
cout << (b ? "YES" : "NO") << '\n';
}
return 0;
}
```
## 砖块摆放
有 $n$ 个砖块,第 $i$ 个砖块颜色为 $s_i$(用 $\texttt{A,B,C}$ 表示,转换成数字为 $0,1,2$)。进行 $n-1$ 次变换,每次变换会将元素数量 $n$ 变为 $n-1$。设 $a$ 为变换前的序列,则变换后的序列 $b$ 满足 $b_i=-(a_i+a_{i+1})\bmod 3$。
---
我们观察 $-(a_i+a_{i+1})\bmod 3$ 这个式子,可以将 $\bmod 3$ 去掉最后做(因为取模的性质),然后再将负号去掉,因为负负得正,有偶数个负号即可消掉,因此仅当 $2|n$ 时答案需要额外加上负号。
观察 $a_i+a_{i+1}$,不难发现它跟杨辉三角非常相像。令 $T_{i,j}$ 为杨辉三角内第 $i$ 行第 $j$ 列的数,那么可以画图找到规律对于答案 $a_i$ 的系数正好为 $T_{i,j}$。又 ${} \tbinom{i-1}{j-1}=T_{i,j} {}$,因此我们需要快速求组合数。
因为模数 $3$ 是质数,因此考虑费马小定理求逆元($a^{p-1}\equiv 1\pmod p$,其中 $p$ 为质数,通过变换得到 ${} a^{p-2}\equiv \cfrac{1}{a}\pmod p {}$)。由于 $\tbinom{n}{m}=\cfrac{n!}{m!(n-m)!}$,因此我们预处理 $i!$ $(0\le i\le n)$,然后通过费马小定理将 $\cfrac{1}{a}$ 转化成 $a_{p-2}$,即 $\times a$,接着就可以使用公式快速求出 $\tbinom{n}{m}$,将其转化为系数累加。
但是在实际的实现当中我们会出现很大的问题。比如 $\cfrac{4!}{3!}$(等同于 $\cfrac{24}{12}$),我们使用费马小定理将原式转化为 $24\times 12$,然后对 $3$ 取模……变成 $0$ 了,但是我们直接使用除法可以得到 $4\bmod 3=1$。这时我们垂死病中惊坐起,突然发现小丑竟是我自己,因为费马小定理使用不仅需要 $p$ 为质数,还要求 $a\perp b$。同样地,拓展欧几里得求逆元也不能用。你干嘛~哈哈哎哟——
这时我们突然发现我们之前求组合数模数都很大,今天的模数特别小只有 $3$,因此可以手推卢卡斯定理,然后通过递归不断缩小,如果 $n,m$ 较小就直接暴力求。这样,我们就以 $\mathcal O(n\log_3 n)$ 的可爱时间复杂度成功过题了。
$\binom{n}{m}\equiv \binom{\lfloor n/p\rfloor}{\lfloor m/p\rfloor}\times\binom{n\bmod p}{m\bmod p}\pmod p
$
```cpp
#include <fstream>
using namespace std;
using LL = long long;
const LL kMaxN = 2e5 + 5;
ifstream cin("brick.in");
ofstream cout("brick.out");
LL a[kMaxN], t, n, ans;
char s[kMaxN];
LL C(LL n, LL m) {
if (n < m) {
return 0;
}
LL res = 1;
for (LL i = n - m + 1; i <= n; i++) {
res *= i;
}
for (LL i = 1; i <= m; i++) {
res /= i;
}
return res % 3;
}
LL fuck(LL n, LL m) {
if (n < m) {
return 0;
}
if (n <= 10) {
return C(n, m);
}
return fuck(n / 3, m / 3) * C(n % 3, m % 3) % 3;
}
int main() {
cin.tie(0)->sync_with_stdio(0);
for (cin >> t; t; t--) {
cin >> n >> (s + 1), ans = 0;
for (LL i = 1; i <= n; i++) {
a[i] = s[i] == 'A' ? 0 : s[i] == 'B' ? 1 : 2;
}
for (LL i = 1; i <= n; i++) {
ans = ans + (fuck(n - 1, i - 1) * a[i] % 3) % 3;
}
n % 2 || (ans = (-ans % 3 + 3) % 3);
cout << (ans == 0 ? 'A' : ans == 1 ? 'B' : 'C') << '\n';
}
return 0;
}
```
## 学习 LIS
有长度为 $n$ 的数组 $a$,满足 $\forall i\le n,a_i\in[1,m]$。已知对于数组 $a$ 的位置 $i$,以 $i$ 结尾的最长上升子序列长度为 $l_i$,求满足条件的 $a$ 的数量。
---
使用暴力,通过 dfs 给每一个数都选择可能得值,在搜的时候就使用可行性剪枝去掉不满足条件的数。对于每一个数,一共有 $m$ 种选择,需要选择 $n$ 个数,因此最坏时间复杂度为 $\mathcal O(m^n)$,但是由于使用了可行性剪枝完全可以骗过 $1\le n,m\le 10$ 的数据。好渴鹅考场的写法写错了,炸裂 $0$ 分。
## 战略轰炸
没有完全看懂题目,连文件都没有创建,$0$ 分。出题人,我爱你❤。
## 总结
- **排名**:$2$;
- **比赛分数**:$200/400$;
- **情况**:相比 [[2024.10.7 模拟赛]],一般;
- **问题**:序列 dp 能力较弱。