A 小红的区间计数
知识点:计数
三个数字分别判断一下是否在 $[l,r]$ 的区间内,是则 $-1$ ,注意判断这三个数字是否有重复。
时间复杂度 $\mathcal{O}(1)$
void solve() {
int l, r;
vector<int> a(3);
cin >> a[0] >> a[1] >> a[2] >> l >> r;
sort(a.begin(), a.end());
a.erase(unique(a.begin(), a.end()), a.end());
auto In = [&](int x, int l, int r) { return l <= x && x <= r; };
int ans = r - l + 1;
for (auto v : a) ans -= In(v, l, r);
cout << ans << endl;
}
B 小红的牛魔
知识点:栈
本质括号匹配,用栈模拟一下,每次判断一下栈顶的几个元素是否可删即可,这里为了实现方便使用 $\text{string}$ 来实现栈。
时间复杂度 $\mathcal{O}(n)$
void solve() {
int n;
string s;
cin >> n >> s;
string st = "";
for (auto v : s) {
st += v;
if (st.size() >= 3) {
if (st.substr(st.size() - 3) == "niu") st.pop_back(), st.pop_back(), st.pop_back();
}
if (st.size() >= 2) {
if (st.substr(st.size() - 2) == "mo") st.pop_back(), st.pop_back();
}
}
cout << (st.empty() ? "Yes" : "No") << endl;
}
C 小红的矩阵计数
知识点:枚举
不妨把 $\text{L}$ 块补全为 $2\times 2$ 的正方形,用相对于左上格子的相对位移表示 $L$ 块,枚举左上格子计数即可。
时间复杂度 $\mathcal{O}(nm)$
void solve() {
int n, m;
cin >> n >> m;
vector<string> g(n);
for (int i = 0; i < n; ++i) cin >> g[i];
auto check = [&](int x, int y) { return 0 <= x && x < n && 0 <= y && y < m; };
auto calc = [&](vector<PII> sp) {
int res = 0;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
unordered_set<int> st;
for (auto [u, v] : sp) {
int nx = i + u, ny = j + v;
if (!check(nx, ny)) {
break;
}
st.insert(g[nx][ny] - '0');
}
if (st.size() == 3) res++;
}
}
return res;
};
cout << calc({{0, 1}, {1, 1}, {1, 0}}) + calc({{0, 0}, {0, 1}, {1, 1}}) + calc({{1, 1}, {1, 0}, {0, 0}}) + calc({{1, 0}, {0, 0}, {0, 1}}) << endl;
}
D/E 小红的排序(hard)
知识点:图论,并查集
对于每一组交换,必然有 $(a,b)(b,c)\rightarrow (a,c)$ 。那么我们对所有可交换的元素连边,连通块内的元素显然两两可达。最后检查 $a[i]$ 和 $i$ 是否在同一个连通块内即可,使用并查集查询即可。
时间复杂度 $\mathcal{O}(n)$
void solve() {
int n, x, y;
cin >> n >> x >> y;
vector<int> p(n + 1), pos(n + 1);
for (int i = 1; i <= n; ++i) cin >> p[i], pos[p[i]] = i;
DSU dsu(n);
for (int i = 1; i <= n; ++i) {
if (i + x <= n) dsu.merge(i, i + x);
if (i + y <= n) dsu.merge(i, i + y);
}
for (int i = 1; i <= n; ++i) {
if (!dsu.same(i, pos[i])) {
cout << "No" << endl;
return;
}
}
cout << "Yes" << endl;
}
F 小红的三角形构造
知识点:数学,构造
对于一个直角三角形,我们可以用
$$\begin{cases}a = n^2-m^2\\b = 2nm\\c = n^2+m^2\end{cases}$$
进行描述。
所以:
- 如果 $x$ 是奇数,我们令 $a = x = n^2-m^2 = (n+m)(n-m)$ ,取 $n-m = 1, n+m=x$ ,解得
$$n = \frac{x+1}{2}, m = \frac{x-1}{2}$$
计算出 $b,c$ 即可。 - 如果 $x$ 是偶数,我们令 $b=x=2mn$ ,取 $n=\frac{x}{2},m=1$ ,计算 $a,c$ 即可。
时间复杂度 $\mathcal{O}(1)$
void solve() {
int x;
cin >> x;
if (x == 1 || x == 2)
cout << "No" << endl;
else if (x & 1) {
int n = (x + 1) / 2, m = (x - 1) / 2;
cout << "Yes" << endl;
cout << n * n - m * m << " " << 2 * n * m << " " << n * n + m * m << endl;
} else {
int n = x / 2, m = 1;
cout << "Yes" << endl;
cout << n * n - m * m << " " << 2 * n * m << " " << n * n + m * m << endl;
}
}
G 小红的生成树构造
知识点:生成树,构造,连通块,并查集
我们可以将题目的条件转化为:若我们将所有点分为两类:$S1={A,B}$ 和 $S2={C,D}$ ,则在这条连接 $A$ 和 $B$ 的路径上,所有经过的节点都必须属于
$S1$ ,也就是说,$A$ 和 $B$ 的匹配必须完全在 $S1$ 的同色连通块内部发生;$C$ 和 $D$ 的匹配必须完全在 $S2$ 的同色连通块内部发生。
所以,在任何合法的生成树中,去除连接 $S1$ 和 $S2$ 之间的所有边后,我们会得到一个只由 $S1$ 节点构成与只由 $S2$ 节点构成的森林。对于每棵处于 $S1$ 中的树,它要么为空,要么必须同时包含至少一个 $A$ 和至少一个 $B$ 。因为只有这样,树中的 $A$ 才能通向 $B$ ,且路径仅由 $S1$ 内节点构成。同理,每棵处于 $S2$ 中的树也必须同时包含至少一个 $C$ 和至少一个 $D$ 。
所以我们先连所有同组边,检查每一个连通块内是否包含该组的每组元素至少一个,然后将不同组的连通块之间相连即可。
时间复杂度 $\mathcal{O}(m)$
struct DSU {
vector<int> f, siz;
vector<int> mask;
DSU() {}
DSU(int n) {
init(n);
}
void init(int n) {
f.resize(n + 1);
mask.resize(n + 1);
iota(f.begin(), f.end(), 0);
siz.assign(n + 1, 1);
}
int find(int x) {
return f[x] == x ? x : f[x] = find(f[x]);
}
void merge(int x, int y) {
x = find(x), y = find(y);
if (x == y) return;
mask[x] |= mask[y];
f[y] = x;
siz[x] += siz[y];
}
bool same(int x, int y) {
return find(x) == find(y);
}
int size(int x) {
return siz[find(x)];
}
};
void init() {}
void solve() {
int n, m;
string s;
cin >> n >> m >> s;
vector<int> type(n + 1);
DSU dsu(n);
for (int i = 0; i < n; ++i) {
if (s[i] == 'A') {
type[i + 1] = 0, dsu.mask[i + 1] = 1;
} else if (s[i] == 'B') {
type[i + 1] = 0, dsu.mask[i + 1] = 2;
} else if (s[i] == 'C') {
type[i + 1] = 1, dsu.mask[i + 1] = 4;
} else if (s[i] == 'D') {
type[i + 1] = 1, dsu.mask[i + 1] = 8;
}
}
vector<PII> edges(m), ans;
for (int i = 0; i < m; ++i) cin >> edges[i].fi >> edges[i].se;
for (auto [u, v] : edges) {
if (type[u] == type[v]) {
if (!dsu.same(u, v)) {
dsu.merge(u, v);
ans.pb({u, v});
}
}
}
for (int i = 1; i <= n; i++) {
if (dsu.f[i] == i) {
if (type[i] == 0) {
if ((dsu.mask[i] & 1) == 0 || (dsu.mask[i] & 2) == 0) {
cout << "No" << endl;
return;
}
} else {
if ((dsu.mask[i] & 4) == 0 || (dsu.mask[i] & 8) == 0) {
cout << "No" << endl;
return;
}
}
}
}
for (auto [u, v] : edges) {
if (type[u] != type[v]) {
if (!dsu.same(u, v)) {
dsu.merge(u, v);
ans.pb({u, v});
}
}
}
if (ans.size() != n - 1) {
cout << "No" << endl;
return;
}
cout << "Yes" << endl;
for (auto [u, v] : ans) {
cout << u << " " << v << endl;
}
}