A.01 Matrix Again(思维)
题意:
有一个 N×NN \times NN×N 网格。让 (i,j)(i, j)(i,j) 表示从上往下第 iii 行,从左往上第 jjj 列的单元格。
您需要在每个单元格中填入 000 或 111 。请构建一个满足以下所有条件的填充网格的方法:
- 单元格 (A1,B1),(A2,B2)…(AM,BM)(A_1,B_1),(A_2,B_2)\dots (A_M,B_M)(A1,B1),(A2,B2)…(AM,BM) 中包含 111 。
- 第 iii 行中的整数总和为 MMM 。 (1≤i≤N)(1 \le i \le N)(1≤i≤N)
- 第 iii 列中的整数总和为 MMM 。 (1≤i≤N)(1 \le i \le N)(1≤i≤N)
可以证明,在这个问题的约束条件下,至少有一种填充网格的方法可以满足条件。
分析:
设 SkS_kSk 是单元格 (i,j)(i,j)(i,j) 的集合,满足 i+j=k mod Ni + j = k \bmod Ni+j=kmodN 。现在,如果我们从 SkS_kSk 中选择 MMM 个不同的单元格,并在这些单元格中写入 111 ,则行和与列和将为 MMM 。
那么接下来就是为每个 iii 加入 SAi+Bi mod NS_{A_i + B_i \bmod N}SAi+BimodN ,需要注意如果有重复的Ai+Bi mod NA_i + B_i \bmod NAi+BimodN 中,则需要相应添加 SkS_kSk 。
代码:
#include <bits/stdc++.h>
using namespace std;
int n, m;
int main() {
int T;
T = 1;
while (T--) {
cin >> n >> m;
vector<bool> vis(n);
for (int i = 1; i <= m; i++) {
int x, y;
cin >> x >> y;
x--, y--;
vis[(x + y) % n] = 1;
}
vector<int> ans;
for (int i = 0; i < n; i++)
if (vis[i])
ans.push_back(i);
for (int i = 0; i < n; i++)
if (!vis[i] && ans.size() < m)
ans.push_back(i);
cout << n * m << endl;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
int x = i, y = (ans[j] - i + n) % n;
cout << x + 1 << " " << y + 1 << endl;
}
}
}
return 0;
}
B.Simple Math 4(数学)
题意:
求 2N2^N2N 除以 2M−2K2^M - 2^K2M−2K 的余数的最后一位数字。
分析:
如果是 N≥MN \ge MN≥M ,我们可以变换 2N≡2N−2N−M(2M−2K)≡2N−(M−K)( mod 2M−2K)2^N \equiv 2^N - 2^{N-M}(2^M - 2^K) \equiv 2^{N-(M-K)} (\bmod\ 2^M - 2^K)2N≡2N−2N−M(2M−2K)≡2N−(M−K)(mod 2M−2K) ,这样就可以把 NNN 换成 N−(M−K)N-(M-K)N−(M−K) 。重复这一操作,我们就可以将其简化为 N<MN < MN<M 的情况。
那么,如果是 N,K=M−1N,K = M-1N,K=M−1 ,我们就有 2N=2M−2K2^N = 2^M - 2^K2N=2M−2K ,所以答案是 000 。否则,有 2N<2M−2K2^N < 2^M - 2^K2N<2M−2K ,所以答案是 2N mod 102^N \bmod 102Nmod10 。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod = 998244353;
LL binpow(LL a, LL b, LL m) {
a %= m;
LL res = 1;
while (b > 0) {
if (b & 1)
res = res * a % m;
a = a * a % m;
b >>= 1;
}
return res;
}
LL n, m;
int main() {
int T;
cin >> T;
while (T--) {
LL k;
cin >> n >> m >> k;
n -= max(0ll, n - k) / (m - k) * (m - k);
if (n == m - 1 && k == m - 1)
cout << "0" << endl;
else
cout << binpow(2, n, 10) << endl;
}
return 0;
}
C.Max Permutation(图论)
题意:
输出满足以下条件的 (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) 的数量,并将答案对 998244353998244353998244353 取模。
- max(PAi,PBi)=Ci (1≤i≤M)\max(P_{A_i},P_{B_i}) = C_i\ (1 \le i \le M)max(PAi,PBi)=Ci (1≤i≤M) .
分析:
我们构建一个有 NNN 个顶点的图 GGG ,其中每个 iii 顶点 AiA_iAi 和 BiB_iBi 之间都有一条权重为 CiC_iCi 的边。
如果有 max(PAi,PBi)=Ci\max(P_{A_i},P_{B_i}) = C_imax(PAi,PBi)=Ci ,那么必须有 PAi,PBi≤CiP_{A_i},P_{B_i} \le C_iPAi,PBi≤Ci 。据此,对于每一个 iii 我们都可以推导出一个形式为 Pi≤XiP_i \le X_iPi≤Xi 的条件。(如果顶点 iii 是 GGG 中的一个孤立点,那么 Xi=∞X_i = \inftyXi=∞ )。
假设Pi=kP_i = kPi=k 并且我们按照 k=N,N−1,...,1k = N,N-1,...,1k=N,N−1,...,1的顺序依次处理 。在此过程中,我们进行以下情况的区分。
- 如果有两个或两个以上的 iii 满足 Ci=kC_i = kCi=k。那么所有权重为 kkk 的边都必须有一个顶点 vvv 作为端点。这里需要注意的是,如果存在这样一个顶点 vvv ,那么它就是唯一确定的。如果 vvv 不存在或 Xv<kX_v < kXv<k 不存在,那么答案就是 000 。否则,我们设为 Pv=kP_v = kPv=k。
- 如果只有一个 iii 满足 Ci=kC_i = kCi=k。则在该边的端点中选择一个 Xj≥kX_j \ge kXj≥k 并设置 Pj=kP_j = kPj=k 。如果两个端点都满足条件,那么无论我们选择哪一个,情况都是一样的,因此我们将答案乘以 222 并设置 Pj=kP_j = kPj=k 。如果两个端点都不符合条件,答案就是000 。
- 如果没有iii满足 Ci=kC_i = kCi=k。我们从 Xj≥kX_j \ge kXj≥k 和 PjP_jPj 尚未确定的顶点中选择一个顶点,并设置为 Pj=kP_j = kPj=k 。如果不存在这样的顶点,答案就是 000 。如果存在多个这样的顶点,那么无论我们选择哪个顶点,情况都是一样的,我们将答案乘以候选顶点的数量,并为某个顶点设置 Pj=kP_j = kPj=k 。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 3e5 + 10;
const int MOD = 998244353;
int n, m;
int tmp[MAXN];
int a[MAXN];
int u, v, w;
vector<pair<int, int> > e[MAXN];
LL ans = 1, res;
int main() {
int T;
T = 1;
while (T--) {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
tmp[i] = n;
}
for (int i = 0; i < m; i++) {
cin >> u >> v >> w;
e[w].push_back(make_pair(u, v));
tmp[u] = min(tmp[u], w);
tmp[v] = min(tmp[v], w);
}
for (int i = 1; i <= n; i++) {
a[tmp[i]]++;
}
for (int i = n; i >= 1; i--) {
res += a[i];
if (e[i].size() >= 2) {
int tmp1 = -1;
if (e[i][0].first == e[i][1].first || e[i][0].first == e[i][1].second) {
tmp1 = e[i][0].first;
} else if (e[i][0].second == e[i][1].first || e[i][0].second == e[i][1].second) {
tmp1 = e[i][0].second;
} else {
ans = 0;
}
for (int j = 2; j < e[i].size(); j++) {
if (e[i][j].first != tmp1 && e[i][j].second != tmp1) {
ans = 0;
}
}
if (tmp[tmp1] < i) {
ans = 0;
}
res--;
} else if (e[i].size() == 1) {
int num = 0;
if (tmp[e[i][0].first] >= i) {
num++;
}
if (tmp[e[i][0].second] >= i) {
num++;
}
ans = (ans * num) % MOD;
if (num) {
res--;
}
} else {
ans = (ans * res) % MOD;
if (res) {
res--;
}
}
}
cout << ans << endl;
}
return 0;
}
D.Swap Permutation (数学)
题意:
给你一个 (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) 。你将执行以下操作 MMM 次:
- 选择一对整数 (i,j)(i, j)(i,j) ,使得 1≤i<j≤N1 \le i < j \le N1≤i<j≤N ,然后交换 PiP_iPi 和 PjP_jPj 。
有 (N(N−1)2)M\left(\frac{N(N-1)}{2}\right)^M(2N(N−1))M 个可能的操作序列。对于其中的每一个,考虑所有运算后的值 ∑i=1N−1∣Pi−Pi+1∣\sum\limits_{i=1}^{N-1} |P_i - P_{i+1}|i=1∑N−1∣Pi−Pi+1∣ 。求所有这些值的和,并对998244353998244353998244353取模 。
分析:
将问题转化成:把PPP 中小于或等于 kkk 的值替换为 000 ,将大于 kkk 的值替换为 111 ,再计算 ∑i=1N−1∣Pi−Pi+1∣\sum\limits_{i=1}^{N-1} |P_i - P_{i+1}|i=1∑N−1∣Pi−Pi+1∣ 的和,并将所有 k(1≤k≤N−1)k(1 \le k \le N-1)k(1≤k≤N−1) 的和相加。
接下来解决 PPP 的所有元素都是 000 或 111 的问题。考虑找出每个 iii 在操作后 ∣Pi−Pi+1∣=1|P_i - P_{i+1}| = 1∣Pi−Pi+1∣=1 的序列数量。这可以通过对三个状态进行矩阵指数运算来实现,三个状态分别代表 PiP_iPi 和 Pi+1P_{i+1}Pi+1 中有 j(0≤j≤2)j(0 \le j \le 2)j(0≤j≤2) 个零。
对所有 i,ki,ki,k 进行上述运算的时间复杂度为 O(N2logM)\mathrm{O}(N^2 \log M)O(N2logM) ,但如果使用累积和或类似方法,事先计算出每个 kkk 中 iii 的个数,使得初始状态中 PiP_iPi 和 Pi+1P_{i+1}Pi+1 中不大于 kkk 的元素个数为 jjj ,就可以在 O(logM)\mathrm{O}(\log M)O(logM) 中找到每个 kkk 的答案。
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 10;
const int mod = 998244353;
struct MAT {
int z[3][3];
MAT() {
memset(z, 0, sizeof(z));
}
MAT operator*(MAT y) {
MAT x = *this, ans;
for (int i = 0; i < 3; ++i) {
for (int k = 0; k < 3; ++k) {
for (int j = 0; j < 3; ++j) {
ans.z[i][j] = (ans.z[i][j] + 1ll * x.z[i][k] * y.z[k][j]) % mod;
}
}
}
return ans;
}
MAT operator^(int y) {
MAT x = *this, ans;
for (int i = 0; i < 3; ++i) {
ans.z[i][i] = 1;
}
while (y) {
if (y & 1) {
ans = ans * x;
}
x = x * x;
y >>= 1;
}
return ans;
}
};
int p[maxn], v[maxn], c[4];
int main() {
int T;
T = 1;
while (T--) {
int n, m;
cin >> n >> m;
for (int i = 1, x; i <= n; ++i) {
cin >> x;
p[x] = i;
}
int ans = 0;
c[0] = n - 1;
for (int i = 1; i < n; ++i) {
int t = p[i];
if (t != 1) {
--c[(v[t - 1] << 1) | v[t]];
}
if (t != n) {
--c[(v[t] << 1) | v[t + 1]];
}
v[t] = 1;
if (t != 1) {
++c[(v[t - 1] << 1) | v[t]];
}
if (t != n) {
++c[(v[t] << 1) | v[t + 1]];
}
MAT tmp;
tmp.z[0][0] = (1ll * n * (n - 1) / 2 - 2 * i) % mod;
tmp.z[0][1] = 2 * i;
tmp.z[1][0] = n - i - 1;
tmp.z[1][1] = (1ll * n * (n - 1) / 2 - (n - i - 1) - (i - 1)) % mod;
tmp.z[1][2] = i - 1;
tmp.z[2][1] = (n - i) * 2;
tmp.z[2][2] = (1ll * n * (n - 1) / 2 - (n - i) * 2) % mod;
MAT tn = tmp ^ m;
ans = (ans + 1ll * tn.z[0][1] * c[0] % mod + 1ll * tn.z[1][1] * (c[1] + c[2]) % mod +
1ll * tn.z[2][1] * c[3] % mod) % mod;
}
cout << ans << endl;
}
return 0;
}
赛后交流
在比赛结束后,会在交流群中给出比赛题解,同学们可以在赛后查看题解进行补题。
群号: 704572101,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。

6724

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



