2023牛客暑期多校训练营3 ABDHJ
A
题解
知识点:数学。
当 \(x = 0\) 时,当且仅当 \(y = 0\) 可行。
当 \(x \neq 0\) 时,一定可行,答案为 \(|x-y|\) 。
时间复杂度 \(O(1)\)
空间复杂度 \(O(1)\)
代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
string x, y;
cin >> x >> y;
ll X = 0, Y = 0;
for (int i = 0;i < x.size();i++) (X <<= 1) |= x[i] - '0';
for (int i = 0;i < y.size();i++) (Y <<= 1) |= y[i] - '0';
if (X == 0) {
if (Y == 0) cout << 0 << '\n';
else cout << -1 << '\n';
}
else cout << abs(X - Y) << '\n';
return 0;
}
B
题解
知识点:排列组合,计数dp。
注意到,最后的排列一定是一段小数 \([1,n]\) ,一段大数 \([n+1,2n]\) 交替。
考虑设 \(f_{i,j,0/1}\) 表示填了 \(i\) 个小数, \(j\) 个大数,最后一段是小数或大数段的合法方案数(注意这里没包括最后一个错误的牌),有转移方程:
if (k <= i) f[i][j][0] += f[i - k][j][1] * CNM::C(n - i + k, k);
if (k <= j) f[i][j][1] += f[i][j - k][0] * CNM::C(n - j + k, k);
其中 \(k\) 为最后一段数字的个数。
统计答案时,我们考虑每个位置猜对时产生的贡献。对于前 \(i\) 个小数, \(j\) 个大数猜对时的方案数量为:
此时的所有方案,一定会在第 \(i+j\) 的位置产生一张猜对的牌的贡献。
最后我们需要加上每个排列最后一张错误的牌,共 \((2n)! - (f_{n,n,0} + f_{n,n,1})\) 种方案会产生一张错误的牌。
时间复杂度 \(O(n^3)\)
空间复杂度 \(O(n^2)\)
代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
struct Modint {
static int P;
int val;
Modint(int _val = 0) :val(_val %P) { format(); }
Modint(ll _val) :val(_val %P) { format(); }
//if val in [-P,2P)
Modint &format() {
if (val < 0) val += P;
if (val >= P) val -= P;
return *this;
}
Modint inv()const { return qpow(*this, P - 2); }
Modint &operator+=(const Modint &x) { val += x.val;return format(); }
Modint &operator-=(const Modint &x) { val -= x.val;return format(); }
Modint &operator*=(const Modint &x) { val = 1LL * val * x.val % P;return *this; }
Modint &operator/=(const Modint &x) { return *this *= x.inv(); }
friend Modint operator-(const Modint &x) { return { -x.val }; }
friend Modint operator+(Modint a, const Modint &b) { return a += b; }
friend Modint operator-(Modint a, const Modint &b) { return a -= b; }
friend Modint operator*(Modint a, const Modint &b) { return a *= b; }
friend Modint operator/(Modint a, const Modint &b) { return a /= b; }
friend Modint qpow(Modint a, ll k) {
Modint ans = 1;
while (k) {
if (k & 1) ans = ans * a;
k >>= 1;
a = a * a;
}
return ans;
}
friend istream &operator>>(istream &is, Modint &x) {
ll _x;
is >> _x;
x = { _x };
return is;
}
friend ostream &operator<<(ostream &os, const Modint &x) { return os << x.val; }
};
/// 自动取模整数,O(logk)快速幂、O(logP)逆元、O(1)运算
int P;
namespace CNM {
const int N = 3e2 + 7;
int c[N][N];
void init(int n) {
for (int i = 0;i <= n;i++)
for (int j = 0;j <= i;j++)
c[i][j] = 0 < j && j < i ? (c[i - 1][j - 1] + c[i - 1][j]) % P : 1;
}
int C(int n, int m) {
if (n == m && m == -1) return 1; //* 隔板法特判
if (n < m || m < 0) return 0;
return c[n][m];
}
}
int Modint::P = 1e9 + 7;
Modint f[307][307][2];
Modint fact[607];
bool solve() {
int n;
cin >> n >> P;
Modint::P = P;
CNM::init(n);
fact[0] = 1;
for (int i = 1;i <= 2 * n;i++) fact[i] = fact[i - 1] * i;
for (int i = 0;i <= n;i++) for (int j = 0;j <= n;j++) f[i][j][0] = f[i][j][1] = 0;
f[0][0][0] = f[0][0][1] = 1;
Modint ans = 0;
for (int i = 0;i <= n;i++) {
for (int j = 0;j <= n;j++) {
for (int k = 1;k <= n;k++) {
if (k <= i) f[i][j][0] += f[i - k][j][1] * CNM::C(n - i + k, k);
if (k <= j) f[i][j][1] += f[i][j - k][0] * CNM::C(n - j + k, k);
}
if (i + j > 0) ans += (f[i][j][0] + f[i][j][1]) * fact[2 * n - i - j];// 第i+j张牌正确时的贡献 = 至少在i+j前正确时的方案数 * 1
}
}
ans += fact[2 * n]; // 所有排列都会有一张错误的牌
ans -= f[n][n][0] + f[n][n][1]; // 除了全对的排列
cout << ans << '\n';
return true;
}
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
if (!solve()) cout << -1 << '\n';
}
return 0;
}
D
题解
知识点:位运算,数学。
注意到,最终只有全 \(0\) 或 \(1\) 的情况才是合法的。因此考虑从这两种情况逆推,不妨考虑全 \(0\) 情况。
我们发现,无论如何操作,所有行都和第一行或者第一行取反一致(对列一样,这里只需要考虑行即可)。因此,一开始先判断矩阵是否合法,再进行操作次数的计算。
对于操作次数, 最小值为第一行的 \(0,1\) 最少数量加第一列的 \(0,1\) 最少数量。因为,我们先对行操作把一行对齐,此时所有行都是一样的,在对列操作即可。对行操作的次数,即第一列的 \(0,1\) 最少数量;之后,对列操作的次数,即第一行的 \(0,1\) 最少数量。
时间复杂度 \(O(n^2)\)
空间复杂度 \(O(n^2)\)
代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int a[2007][2007];
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n;
cin >> n;
for (int i = 1;i <= n;i++)
for (int j = 1;j <= n;j++) {
char ch;
cin >> ch;
a[i][j] = ch == '1';
}
for (int i = 1;i <= n;i++) {
bool ok = 1, f = a[i][1] == a[1][1];
for (int j = 1;j <= n;j++) ok &= (a[i][j] == a[1][j]) == f;
if (!ok) {
cout << -1 << '\n';
return 0;
}
}
array<int, 4> cnt{};
for (int i = 1;i <= n;i++) {
if (a[1][i]) cnt[1]++;
else cnt[0]++;
if (a[i][1]) cnt[2 + 1]++;
else cnt[2 + 0]++;
}
cout << min(cnt[0], cnt[1]) + min(cnt[2], cnt[3]) << '\n';
return 0;
}
H
题解
知识点:数论。
显然,操作可以使得数字任意分配,但总和不变,因此考虑总和能否构造为质数序列。
哥德巴赫猜想:对于一个大于等于 \(4\) 的偶数,总能分为两个质数之和。(人类探索范围没有反例)
分类讨论:
-
当 \(n=1\) 时,判断是否为质数即可。
-
当 \(n=2\) 时,若 \(sum < 4\) 则无解。
否则,若 \(sum\) 是奇数,那么 \(sum-2\) 是质数即可;若 \(sum\) 是偶数,根据哥德巴赫猜想一定有解。
-
当 \(n \geq 3\) 时,若 \(sum < 2n\) 则无解。
否则,若 \(sum\) 是奇数,前面放 \(n-3\) 个 \(2\) 和 \(1\) 个 \(3\) ,最后还剩大于等于 \(4\) 的偶数,根据哥德巴赫猜想一定有解 ;若 \(sum\) 是偶数,根据哥德巴赫猜想一定有解。
综上 \(sum \geq 2n\) 一定有解。
时间复杂度 \(O(n)\)
空间复杂度 \(O(1)\)
代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
bool isPrime(int n) {
if (n == 2) return 1;
if (n == 1) return 0;
for (int i = 2;i * i <= n;i++) if (!(n % i)) return 0;
return 1;
}
/// 试除法,O(n^(1/2)),枚举[1,sqrt(n)]作为质因子判断素数
int a[1007];
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n;
cin >> n;
ll sum = 0;
for (int i = 1;i <= n;i++) cin >> a[i], sum += a[i];
if (n == 1) {
if (isPrime(a[1])) cout << "Yes" << '\n';
else cout << "No" << '\n';
return 0;
}
if (n == 2) {
if (sum >= 2 * n) {
if (sum & 1) {
if (isPrime(sum - 2)) cout << "Yes" << '\n';
else cout << "No" << '\n';
}
else cout << "Yes" << '\n';
}
else cout << "No" << '\n';
return 0;
}
if (sum >= 2 * n) {
if (sum & 1) {
if (isPrime(sum - (n - 1) * 2)) cout << "Yes" << '\n';
else if (sum - (n - 3) * 2 - 3 >= 4) cout << "Yes" << '\n';
else cout << "No" << '\n';
}
else cout << "Yes" << '\n';
}
else cout << "No" << '\n';
return 0;
}
J
题解
知识点:拓扑排序。
显然,若原图是dag,拓扑序就是最终结果。
否则,一定需要两个排名序列,直接输出 \(1\) 到 \(n\) 正序反序即可。
实现用一次拓扑排序即可完成判环和拓扑序。
时间复杂度 \(O(n+m)\)
空间复杂度 \(O(n+m)\)
代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e6 + 7;
int n, m;
vector<int> g[N];
int deg[N];
int ans[N], cnt;
queue<int> q;
void toposort() {
cnt = 0;
for (int i = 1;i <= n;i++) deg[i] = 0;
for (int i = 1;i <= n;i++) for (auto v : g[i]) deg[v]++;
for (int i = 1;i <= n;i++) if (!deg[i]) q.push(i);
while (!q.empty()) {
int u = q.front();
q.pop();
ans[++cnt] = u;
for (auto v : g[u]) {
deg[v]--;
if (!deg[v]) q.push(v);
}
}
}
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> m;
for (int i = 1;i <= m;i++) {
int u, v;
cin >> u >> v;
g[u].push_back(v);
}
toposort();
if (cnt == n) {
cout << 1 << '\n';
for (int i = 1;i <= n;i++) cout << ans[i] << " \n"[i == n];
}
else {
cout << 2 << '\n';
for (int i = 1;i <= n;i++) cout << i << " \n"[i == n];
for (int i = 1;i <= n;i++) cout << n + 1 - i << " \n"[i == n];
}
return 0;
}
本文来自博客园,作者:空白菌,转载请注明原文链接:https://www.cnblogs.com/BlankYang/p/17582861.html