注意:
\color{red}{注意:}
注意: 个人博客传送门
L2-001 紧急救援
思路:
- 首先,看到这个 n n n 的范围,我们要想到哪几种算法可以通过
- 朴素版 d i j k s t r a dijkstra dijkstra 和 F l o y d Floyd Floyd 都可以通过,但本题使用 F l o y d Floyd Floyd 不好处理,所以我们选用 d i j s t r a dijstra dijstra
- 然后我们跑一遍 d i j k s t r a dijkstra dijkstra ,将所有到 S S S 的最短距离求出来
- 然后我们 d f s dfs dfs 一遍,只要走到了 D D D 点,就个数加 1 1 1,同时更新最大的人数和路径
时间复杂度: O ( n 2 ) O(n^2) O(n2)
#include <bits/stdc++.h>
using i64 = long long;
constexpr int inf = 0x3f3f3f3f;
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N, M, S, D;
std::cin >> N >> M >> S >> D;
std::vector<int> a(N + 1);
for (int i = 0; i < N; i++) {
std::cin >> a[i];
}
std::vector d(N + 1, std::vector<int>(N + 1));
for (int i = 0; i <= N; i++) {
for (int j = 0; j <= N; j++) {
if (i == j) {
d[i][j] = 0;
} else {
d[i][j] = inf;
}
}
}
for (int i = 0; i < M; i++) {
int u, v, w;
std::cin >> u >> v >> w;
d[u][v] = w; d[v][u] = w;
}
std::vector<int> dist(N + 1), vis(N + 1);
auto dijkstra = [&]() {
for (int i = 0; i < N; i++) {
dist[i] = d[S][i];
}
for (int i = 0; i < N; i++) {
int min = inf;
int t = -1;
for (int j = 0; j < N; j++) {
if (!vis[j] && (t == -1 || dist[t] > dist[j])) {
min = dist[j];
t = j;
}
}
for (int j = 0; j < N; j++) {
dist[j] = std::min(dist[j], dist[t] + d[t][j]);
}
vis[t] = true;
}
};
dijkstra();
int max = 0, cnt = 0;
vis.assign(N + 1, 0);
std::vector<int> path, ans;
path.push_back(S);
auto dfs = [&](auto self, int u, int v) -> void {
if (u == D) {
if (v > max) {
max = v;
ans = path;
}
cnt++;
return ;
}
for (int i = 0; i < N; i++) {
if (d[u][i] && !vis[i] && dist[i] == dist[u] + d[u][i]) { // 如果 S 到 i 的距离 == S 到 当前这个点的距离 + 当前这个点到 i 的距离
vis[i] = true;
path.push_back(i);
self(self, i, v + a[i]);
path.pop_back();
vis[i] = false;
}
}
};
dfs(dfs, S, a[S]);
std::cout << cnt << " " << max << "\n";
for (int i = 0; i < ans.size(); i++) {
std::cout << ans[i] << " \n"[i == ans.size() - 1];
}
return 0;
}
L2-002 链表去重
思路:
- 用
bfs
把这个完整的链表建出来,然后我们去重进行重置前后的地址即可
时间复杂度: O ( n ) O(n) O(n)
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N;
std::string st;
std::cin >> st >> N;
std::map<std::string, std::pair<int, std::string>> mp;
for (int i = 0; i < N; i++) {
int w;
std::string x, y;
std::cin >> x >> w >> y;
mp[x] = {w, y};
}
std::vector<std::tuple<std::string, int, std::string>> a;
std::queue<std::string> q;
q.push(st);
while (q.size()) {
auto cur = q.front();
q.pop();
if (mp.count(cur)) {
a.push_back({cur, mp[cur].first, mp[cur].second});
q.push(mp[cur].second);
}
}
std::map<int, int> cnt;
std::vector<int> vis(a.size());
for (int i = 0; i < a.size(); i++) {
auto [x, y, z] = a[i];
if (cnt[abs(y)]) {
continue ;
}
cnt[abs(y)]++;
vis[i] = true;
}
std::vector<std::tuple<std::string, int, std::string>> b;
for (int i = 0; i < a.size(); i++) {
if (vis[i]) {
b.push_back(a[i]);
}
}
for (int i = 0; i < b.size(); i++) {
if (i + 1 < b.size()) {
auto &[x1, y1, z1] = b[i];
auto &[x2, y2, z2] = b[i + 1];
if (z1 != x2) {
z1 = x2;
}
} else {
auto &[x1, y1, z1] = b[i];
z1 = "-1";
}
}
for (int i = 0; i < b.size(); i++) {
auto [x, y, z] = b[i];
std::cout << x << " " << y << " " << z << "\n";
}
b.clear();
for (int i = 0; i < a.size(); i++) {
if (!vis[i]) {
b.push_back(a[i]);
}
}
for (int i = 0; i < b.size(); i++) {
if (i + 1 < b.size()) {
auto &[x1, y1, z1] = b[i];
auto &[x2, y2, z2] = b[i + 1];
if (z1 != x2) {
z1 = x2;
}
} else {
auto &[x1, y1, z1] = b[i];
z1 = "-1";
}
}
for (int i = 0; i < b.size(); i++) {
auto [x, y, z] = b[i];
std::cout << x << " " << y << " " << z << "\n";
}
return 0;
}
L2-003 月饼
思路:
- 贪心,我们存一下每个月饼的收益,然后从大到小排序即可
- 然后逐个选取
时间复杂度: O ( n ) O(n) O(n)
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout << std::fixed << std::setprecision(2);
int N, D;
std::cin >> N >> D;
std::vector<double> a(N), w(N);
for (int i = 0; i < N; i++) {
std::cin >> a[i];
}
std::vector<double> rate(N);
for (int i = 0; i < N; i++) {
std::cin >> w[i];
rate[i] = w[i] * 1.0 / a[i];
}
std::vector<int> p(N);
std::iota(p.begin(), p.end(), 0);
std::sort(p.begin(), p.end(),
[&](int i, int j) {
if (rate[i] == rate[j]) return a[i] > a[j];
return rate[i] > rate[j];
});
double ans = 0;
for (int i = 0; i < N; i++) {
if (a[p[i]] <= D) {
D -= a[p[i]];
ans += w[p[i]];
} else {
ans += D * 1.0 * w[p[i]] / a[p[i]];
break;
}
}
std::cout << ans << "\n";
return 0;
}
L2-004 这是二叉搜索树吗?
思路:
- 首先我们假设这个树是非镜面二叉搜索树,一开始设
ok
为false
,然后我们将其从先序转换为后序,假如发现这个转换后的后序数组长度不为n
,那么就清空,将他按照镜面二叉搜索树的情况来求后续遍历 - 只要以上两种情况中的任何一种合法,就直接输出,否则输出
-1
时间复杂度: O ( n ) O(n) O(n)
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n;
std::cin >> n;
std::vector<int> pre(n);
for (int i = 0; i < n; i++) {
std::cin >> pre[i];
}
bool ok = false;
std::vector<int> post;
auto get = [&](auto self, int root, int tail) -> void {
if (root > tail) return ;
int i = root + 1, j = tail;
if (!ok) {
while (i <= tail && pre[root] > pre[i]) i++;
while (j > root && pre[root] <= pre[j]) j--;
} else {
while (i <= tail && pre[root] <= pre[i]) i++;
while (j > root && pre[root] > pre[j]) j--;
}
if (i - j != 1) return ;
self(self, root + 1, j);
self(self, i, tail);
post.push_back(pre[root]);
};
get(get, 0, n - 1);
if (post.size() != n) {
ok = true;
post.clear();
get(get, 0, n - 1);
}
if (post.size() == n) {
std::cout << "YES\n";
for (int i = 0; i < post.size(); i++) {
std::cout << post[i] << " \n"[i == post.size() - 1];
}
} else {
std::cout << "NO\n";
}
return 0;
}
L2-005 集合相似度
题目大意:
-
其中 N c N_c Nc 是两个集合都有的不相等整数的个数:两个集合交集元素个数
-
N t N_t Nt 是两个集合一共有的不相等整数的个数:两个集合的并集的元素个数
思路:
- 用
set
直接进行集合去重和统计个数
时间复杂度: O ( K l o g M ) O(KlogM) O(KlogM)
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N;
std::cin >> N;
std::vector<std::vector<int>> st(N);
for (int i = 0; i < N; i++) {
int n;
std::cin >> n;
std::set<int> a;
for (int j = 0; j < n; j++) {
int x;
std::cin >> x;
a.insert(x);
}
for (auto x : a) {
st[i].push_back(x);
}
}
int K;
std::cin >> K;
while (K--) {
int a, b;
std::cin >> a >> b;
a--, b--;
std::set<int> k;
for (auto v : st[a]) {
k.insert(v);
}
int ans = 0;
for (auto v : st[b]) {
if (k.count(v)) {
ans++;
}
}
for (auto v : st[b]) {
k.insert(v);
}
printf("%.2lf%\n", ans * 100.0 / k.size());
}
return 0;
}
L2-006 树的遍历
思路:
- 根据中序遍历和后序遍历建树,然后进行
BFS
即可
时间复杂度: O ( n ) O(n) O(n)
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n;
std::cin >> n;
std::vector<int> post(n), in(n);
for (int i = 0; i < n; i++) {
std::cin >> post[i];
}
std::unordered_map<int, int> pos, l, r;
auto build = [&](auto self, int il, int ir, int pl, int pr) -> int {
int root = post[pr]; // 后序遍历的最后一个节点即为根节点
int k = pos[root]; // 在中序遍历中找到根节点的位置
// k - il 为左子树的节点个数
if (il < k) l[root] = self(self, il, k - 1, pl, pl + k - 1 - il);
if (ir > k) r[root] = self(self, k + 1, ir, pl + k - 1 - il + 1, pr - 1);
return root;
};
for (int i = 0; i < n; i++) {
std::cin >> in[i];
pos[in[i]] = i; // 记录中序遍历中节点值与索引的映射关系
}
int root = build(build, 0, n - 1, 0, n - 1);
std::queue<int> q;
q.push(root);
std::vector<int> ans;
while (!q.empty()) {
int t = q.front();
q.pop();
ans.push_back(t);
if (l.count(t)) q.push(l[t]);
if (r.count(t)) q.push(r[t]);
}
for (int i = 0; i < ans.size(); i++) {
std::cout << ans[i] << " \n"[i == ans.size() - 1];
}
return 0;
}
L2-007 家庭房产
思路:
- 将每一个家庭看作一个集合,然后用并查集来维护每一个家庭
- 在合并的过程中,由于题目要求输出家庭里最小的那个编号,那么这个最小的编号就作为祖先节点
- 然后我们遍历,求出每个家庭的信息,最后统计输出即可
时间复杂度: O ( n ) O(n) O(n)
struct DSU {
std::vector<int> f, siz;
DSU() {}
DSU(int n) {
init(n);
}
void init(int n) {
f.resize(n);
std::iota(f.begin(), f.end(), 0);
siz.assign(n, 1);
}
int find(int x) {
while (x != f[x]) {
x = f[x] = f[f[x]];
}
return x;
}
bool same(int x, int y) {
return find(x) == find(y);
}
bool merge(int x, int y) {
x = find(x);
y = find(y);
if (x == y) {
return false;
}
if (x > y) { // 保证小的那个是祖先
f[x] = y;
} else {
f[y] = x;
}
return true;
}
int size(int x) {
return siz[find(x)];
}
};
struct node1 {
int id, fa, mo;
int cnt, area;
int c[5];
};
struct node2 {
int id, peo;
double num, area;
bool ok = false;
};
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N;
std::cin >> N;
DSU f(10010);
std::vector<node1> a(N);
std::vector<int> vis(10010);
for (int i = 0; i < N; i++) {
int k;
std::cin >> a[i].id >> a[i].fa >> a[i].mo >> k;
vis[a[i].id] = true;
if (a[i].fa != -1) {
vis[a[i].fa] = true;
f.merge(a[i].fa, a[i].id);
}
if (a[i].mo != -1) {
vis[a[i].mo] = true;
f.merge(a[i].mo, a[i].id);
}
for (int j = 0; j < k; j++) {
std::cin >> a[i].c[j];
vis[a[i].c[j]] = true;
f.merge(a[i].c[j], a[i].id);
}
std::cin >> a[i].cnt >> a[i].area;
}
std::vector<node2> ans(10000);
for (int i = 0; i < N; i++) {
int pa = f.find(a[i].id);
ans[pa].id = pa;
ans[pa].num += a[i].cnt;
ans[pa].area += a[i].area;
ans[pa].ok = true;
}
int cnt = 0;
for (int i = 0; i < 10000; i++) {
if (vis[i]) {
ans[f.find(i)].peo++;
}
if (ans[i].ok) {
cnt++;
}
}
for (int i = 0; i < 10000; i++) {
if (ans[i].ok) {
ans[i].num = (ans[i].num * 1.0 / ans[i].peo);
ans[i].area = (ans[i].area * 1.0 / ans[i].peo);
}
}
std::sort(ans.begin(), ans.end(), [&](auto i, auto j) {
if (i.area != j.area) {
return i.area > j.area;
}
return i.id < j.id;
});
printf("%d\n", cnt);
for (int i = 0; i < cnt; i++) {
printf("%04d %d %.3f %.3f\n", ans[i].id, ans[i].peo, ans[i].num, ans[i].area);
}
return 0;
}
L2-008 最长对称子串
思路:
- 用字符串 h a s h hash hash 来判断回文串,然后枚举左右端点即可
本题也可以参考一下升级版智乃的前缀、后缀、回文的讲解
时间复杂度: O ( n 2 ) O(n^2) O(n2)
struct Shash{
const i64 base[2] = {29, 31};
const i64 hashmod[2] = {(i64)1e9, 998244353};
std::array<std::vector<i64>, 2> hsh, pwMod;
void init(std::string &s) {
int n = s.size();
s = ' ' + s;
hsh[0].resize(n + 1), hsh[1].resize(n + 1);
pwMod[0].resize(n + 1), pwMod[1].resize(n + 1);
for(int i = 0; i < 2; i++){
pwMod[i][0] = 1;
for(int j = 1; j <= n; j++){
pwMod[i][j] = pwMod[i][j - 1] * base[i] % hashmod[i];
hsh[i][j] = (hsh[i][j - 1] * base[i] + s[j]) % hashmod[i];
}
}
}
std::pair<i64, i64> get(int l, int r) {
std::pair<i64, i64> ans;
ans.first = (hsh[0][r] - hsh[0][l - 1] * pwMod[0][r - l + 1]) % hashmod[0];
ans.second = (hsh[1][r] - hsh[1][l - 1] * pwMod[1][r - l + 1]) % hashmod[1];
ans.first = (ans.first + hashmod[0]) % hashmod[0];
ans.second = (ans.second + hashmod[1]) % hashmod[1];
return ans;
}
bool same(int la, int ra, int lb, int rb){
return get(la, ra) == get(lb, rb);
}
};
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::string s;
std::getline(std::cin, s);
for (auto &i : s) {
if (i == ' ') {
i = 'a';
}
}
std::string t = std::string(s.rbegin(), s.rend());
Shash a, b;
a.init(s); b.init(t);
int n = s.size();
int ans = 1;
for (int i = 1; i <= n; i++) {
for (int j = i; j <= n; j++) {
if (a.get(i, j) == b.get(n - j, n - i)) {
ans = std::max(ans, j - i + 1);
}
}
}
std::cout << ans << "\n";
return 0;
}
L2-009 抢红包
思路:
- 直接模拟就行
时间复杂度: O ( n ) O(n) O(n)
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout << std::fixed << std::setprecision(2);
int N;
std::cin >> N;
std::vector<std::tuple<double, int, int>> a(N + 1);
for (int i = 0; i < N; i++) {
auto &[x, y, z] = a[i + 1];
y = i + 1;
int k;
std::cin >> k;
double sum = 0;
for (int j = 0; j < k; j++) {
int id;
double m;
std::cin >> id >> m;
auto &[x1, y1, z1] = a[id];
x1 += m;
y1 = id;
z1++;
sum += m;
}
x -= sum;
}
std::sort(a.begin() + 1, a.end(), [&](auto i, auto j) {
auto [x1, y1, z1] = i;
auto [x2, y2, z2] = j;
if (x1 == x2) {
if (z1 != z2) {
return z1 > z2;
} else {
return y1 < y2;
}
}
return x1 > x2;
});
for (int i = 1; i <= N; i++) {
auto [x, y, z] = a[i];
std::cout << y << " " << x / 100 << "\n";
}
return 0;
}
L2-010 排座位
思路:
- 用并查集维护一下朋友关系
- 然后用
set
维护一下敌对关系
时间复杂度: O ( M l o g N ) O(MlogN) O(MlogN)
struct DSU {
std::vector<int> f, siz;
DSU() {}
DSU(int n) {
init(n);
}
void init(int n) {
f.resize(n);
std::iota(f.begin(), f.end(), 0);
siz.assign(n, 0);
}
int find(int x) {
while (x != f[x]) {
x = f[x] = f[f[x]];
}
return x;
}
bool same(int x, int y) {
return find(x) == find(y);
}
bool merge(int x, int y) {
x = find(x);
y = find(y);
if (x == y) {
return false;
}
siz[x] += siz[y];
f[y] = x;
return true;
}
int size(int x) {
return siz[find(x)];
}
};
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N, M, K;
std::cin >> N >> M >> K;
DSU f(N + 1);
std::set<std::pair<int, int>> em;
for (int i = 0; i < M; i++) {
int x, y, op;
std::cin >> x >> y >> op;
if (op == -1) {
em.insert({x, y});
em.insert({y, x});
} else {
f.merge(x, y);
}
}
for (int i = 0; i < K; i++) {
int x, y;
std::cin >> x >> y;
if (!f.same(x, y) && !em.count({x, y})) {
std::cout << "OK\n";
} else if (f.same(x, y) && em.count({x, y})) {
std::cout << "OK but...\n";
} else if (em.count({x, y})) {
std::cout << "No way\n";
} else if (f.same(x, y)) {
std::cout << "No problem\n";
}
}
return 0;
}
L2-011 玩转二叉树
思路:
- 按照规则构建一颗二叉树
- 然后
bfs
的时候,将左右节点的输出顺序更换一下即可
时间复杂度: O ( n ) O(n) O(n)
struct Node {
int val;
Node *l;
Node *r;
Node(int x) : val(x), l(nullptr), r(nullptr) {}
};
class Solution {
public:
Node* build(std::vector<int> &ino, std::vector<int> &pre) {
std::unordered_map<int, int> mp;
for (int i = 0; i < ino.size(); i++) {
mp[ino[i]] = i;
}
return buildTree(ino, 0, ino.size() - 1, pre, 0, pre.size() - 1, mp);
}
Node* buildTree(auto &ino, int il, int ir, auto &pre, int pl, int pr, auto mp) {
if (pl > pr) {
return nullptr;
}
int root_val = pre[pl];
Node *root = new Node(root_val);
int tot = mp[root_val]; // 根节点的位置
int ls = tot - il; // 左子树的大小
root->l = buildTree(ino, il, tot - 1, pre, pl + 1, pl + ls, mp);
root->r = buildTree(ino, tot + 1, ir, pre, pl + ls + 1, pr, mp);
return root;
}
};
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n;
std::cin >> n;
std::vector<int> ino(n), pre(n);
for (int i = 0; i < n; i++) {
std::cin >> ino[i];
}
for (int i = 0; i < n; i++) {
std::cin >> pre[i];
}
Solution s;
Node* root = s.build(ino, pre);
std::queue<Node*> q;
q.push(root);
std::vector<int> ans;
while (!q.empty()) {
auto t = q.front();
q.pop();
ans.push_back(t->val);
if (t->r != nullptr) q.push(t->r);
if (t->l != nullptr) q.push(t->l);
}
for (int i = 0; i < ans.size(); i++) {
std::cout << ans[i] << " \n"[i == ans.size() - 1];
}
return 0;
}
L2-013 红色警报
思路:
- 统计这个图的连通分量,然后每次摧毁一个城市,在求一次连通分量;
- 如果摧毁后的连通分量大于摧毁前的连通分量数
+1
,那么,就说明,这个节点导致了这个图不连通了,那么我们就需要输出Red Alert: City k is lost!
,否则输出City k is lost.
- 如果
K
的数量等于N
,那么就说明最后就摧毁完了,输出Game Over.
时间复杂度: O ( K × n 2 ) O(K \times n^2) O(K×n2)
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N, M;
std::cin >> N >> M;
std::vector<std::vector<int>> g(N, std::vector<int>(N));
for (int i = 0; i < M; i++) {
int u, v;
std::cin >> u >> v;
g[u][v] = g[v][u] = 1;
}
std::vector<int> vis(N);
auto dfs = [&](auto self, int u) -> void {
vis[u] = true;
for (int i = 0; i < N; i++) {
if (g[u][i] && !vis[i]) {
self(self, i);
}
}
};
auto count = [&]() {
int cnt = 0;
vis.assign(N, 0);
for (int i = 0; i < N; i++) {
if (!vis[i]) {
dfs(dfs, i);
cnt++;
}
}
return cnt;
};
int cnt = count();
int K;
std::cin >> K;
for (int i = 0; i < K; i++) {
int x;
std::cin >> x;
for (int j = 0; j < N; j++) {
if (g[x][j]) {
g[x][j] = 0;
g[j][x] = 0;
}
}
int t = count();
if (t > cnt + 1) {
std::cout << "Red Alert: City " << x << " is lost!\n";
} else {
std::cout << "City " << x << " is lost.\n";
}
cnt = t;
if (i == N - 1) {
std::cout << "Game Over.\n";
}
}
return 0;
}
L2-014 列车调度
思路:
- 每次将队列的第
i
个数与目前的最大值进行比较,只要出现一个比当前值小的数,我们就把比这个数大的数删掉,这样保证是从大到小的顺序,最后只需要输出这个有序队列的剩下元素的大小-1
即可
时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N;
std::cin >> N;
std::set<int> st;
st.insert(0);
for (int i = 0; i < N; i++) {
int x;
std::cin >> x;
if (x < *st.rbegin()) {
st.erase(*st.upper_bound(x));
}
st.insert(x);
}
std::cout << st.size() - 1 << "\n";
return 0;
}
L2-015 互评成绩
思路:
- 按照规则模拟
时间复杂度: O ( n k ) O(nk) O(nk)
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout << std::fixed << std::setprecision(3);
int N, k, M;
std::cin >> N >> k >> M;
std::vector<double> score;
for (int i = 0; i < N; i++) {
std::vector<double> a(k);
for (int j = 0; j < k; j++) {
std::cin >> a[j];
}
std::sort(a.begin(), a.end());
double avg = std::accumulate(a.begin() + 1, a.end() - 1, 0.0) / (k - 2);
score.push_back(avg);
}
std::sort(score.begin(), score.end());
for (int i = N - M; i < N; i++) {
std::cout << score[i] << " \n"[i == N - 1];
}
return 0;
}
L2-016 愿天下有情人都是失散多年的兄妹
思路:
- 暴搜,统计
x
和y
相差的代数
时间复杂度: O ( n + k D ) O(n + kD) O(n+kD)
struct node {
char sex = '\0';
int faid = -1, moid = -1;
};
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N;
std::cin >> N;
std::vector<node> a(100000);
for (int i = 0; i < N; i++) {
int id;
std::cin >> id;
std::cin >> a[id].sex >> a[id].faid >> a[id].moid;
if (a[id].faid != -1)
a[a[id].faid].sex = 'M';
if (a[id].moid != -1)
a[a[id].moid].sex = 'F';
}
auto dfs = [&](auto self, int x, int y, int d) -> bool {
if (!(x != -1 && y != -1)) {
return true;
}
if (d > 5) return true;
if (x == y) {
return false;
} else {
return self(self, a[x].faid, a[y].moid, d + 1) && self(self, a[x].faid, a[y].faid, d + 1)
&& self(self, a[x].moid, a[y].moid, d + 1) && self(self, a[x].moid, a[y].faid, d + 1);
}
};
int K;
std::cin >> K;
while (K--) {
int x, y;
std::cin >> x >> y;
if (a[x].sex == a[y].sex) {
std::cout << "Never Mind\n";
} else {
if (dfs(dfs, x, y, 1)) {
std::cout << "Yes\n";
} else {
std::cout << "No\n";
}
}
}
return 0;
}
L2-017 人以群分
思路:
- 模拟即可
时间复杂度: O ( n ) O(n) O(n)
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N;
std::cin >> N;
std::vector<int> a(N);
for (int i = 0; i < N; i++) {
std::cin >> a[i];
}
std::sort(a.begin(), a.end());
int mid = N / 2;
i64 sum1 = std::accumulate(a.begin(), a.begin() + mid, 0LL);
i64 sum2 = std::accumulate(a.begin() + mid, a.end(), 0LL);
printf("Outgoing #: %d\n", (N + 1) / 2);
printf("Introverted #: %d\n", N / 2);
printf("Diff = %lld\n", sum2 - sum1);
return 0;
}
L2-019 悄悄关注
思路:
- 直接模拟,判断在不在那个关注集合就行
时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N;
std::cin >> N;
std::set<std::string> st;
for (int i = 0; i < N; i++) {
std::string s;
std::cin >> s;
st.insert(s);
}
int M;
std::cin >> M;
double sum = 0;
std::vector<std::pair<std::string, int>> a(M);
for (int i = 0; i < M; i++) {
std::cin >> a[i].first >> a[i].second;
sum += a[i].second;
}
sum /= M;
std::set<std::string> ans;
for (int i = 0; i < M; i++) {
if (!st.count(a[i].first) && a[i].second > sum) {
ans.insert(a[i].first);
}
}
if (ans.empty()) {
std::cout << "Bing Mei You\n";
return 0;
}
for (auto x : ans) {
std::cout << x << "\n";
}
return 0;
}
L2-020 功夫传人
思路:
- 直接
BFS
或者DFS
时间复杂度: O ( N ) O(N) O(N)
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N;
double Z, r;
std::cin >> N >> Z >> r;
std::map<int, i64> mp;
std::vector<std::vector<int>> g(N);
for (int i = 0; i < N; i++) {
int k;
std::cin >> k;
if (k == 0) {
i64 x;
std::cin >> x;
mp[i] = x;
}
for (int j = 0; j < k; j++) {
int x;
std::cin >> x;
g[i].push_back(x);
}
}
if (mp.count(0)) {
std::cout << i64(Z * mp[0]) << "\n";
return 0;
}
std::queue<std::tuple<int, double>> q;
q.push({0, Z});
double ans = 0;
while (q.size()) {
auto [x, y] = q.front();
q.pop();
for (int v : g[x]) {
if (mp.count(v)) {
double t = mp[v] * y * (1 - r / 100);
q.push({v, t});
ans += t;
} else {
q.push({v, y * (1 - r / 100)});
}
}
}
std::cout << i64(ans) << "\n";
return 0;
}
L2-021 点赞狂魔
思路:
- 可以利用优先队列来存储答案,利用
map
和set
来去重,然后按规则模拟即可
时间复杂度: O ( n l o g l o g n ) O(nloglogn) O(nloglogn)
struct node {
std::string s;
int x;
double avg;
bool operator<(const node &t) const {
if (x == t.x) {
return avg > t.avg;
}
return x < t.x;
}
};
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N;
std::cin >> N;
std::map<std::string, double> avg;
std::map<std::string, std::set<int>> mp;
for (int i = 0; i < N; i++) {
int K;
std::string s;
std::cin >> s >> K;
for (int j = 0; j < K; j++) {
int x;
std::cin >> x;
mp[s].insert(x);
}
avg[s] = K * 1.0 / int(mp[s].size());
}
std::priority_queue<node> q;
for (auto [s, y] : mp) {
q.push({s, y.size(), avg[s]});
}
int cnt = 0;
while (!q.empty()) {
cnt++;
if (cnt == 3) {
std::cout << q.top().s;
break;
}
std::cout << q.top().s << " ";
q.pop();
}
if (cnt < 3) {
for (int i = cnt; i < 3; i++) {
std::cout << '-' << " \n"[i == 2];
}
}
return 0;
}
L2-023 图着色问题
思路:
- 数据范围较小,其实可以直接暴力枚举,然后
check
一下当前位置是否可行,不过我这里用了BFS
来模拟这个check
的过程 - 如果个数不是
K
直接判定为No
时间复杂度: O ( V × N ) O(V \times N) O(V×N)
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int V, E, K;
std::cin >> V >> E >> K;
std::vector<std::vector<int>> adj(V);
for (int i = 0; i < E; i++) {
int u, v;
std::cin >> u >> v;
u--, v--;
adj[u].push_back(v);
adj[v].push_back(u);
}
int N;
std::cin >> N;
while (N--) {
std::set<int> st;
std::vector<int> c(V);
for (int i = 0; i < V; i++) {
std::cin >> c[i];
st.insert(c[i]);
}
std::vector<int> vis(V);
auto bfs = [&](int u) -> bool {
std::queue<int> q;
vis[u] = true;
q.push(u);
while (!q.empty()) {
int x = q.front();
q.pop();
for (int v : adj[x]) {
if (c[v] == c[x]) return false;
if (vis[v]) continue ;
vis[v] = true;
q.push(v);
}
}
return true;
};
if (st.size() == K && bfs(0) && bfs(V - 1)) {
std::cout << "Yes\n";
} else {
std::cout << "No\n";
}
}
return 0;
}
L2-024 部落
思路:
- 并查集,查询集合即可
时间复杂度: O ( N × K ) O(N \times K) O(N×K)
struct DSU {
std::vector<int> f, siz;
DSU() {}
DSU(int n) {
init(n);
}
void init(int n) {
f.resize(n);
std::iota(f.begin(), f.end(), 0);
siz.assign(n, 1);
}
int find(int x) {
while (x != f[x]) {
x = f[x] = f[f[x]];
}
return x;
}
bool same(int x, int y) {
return find(x) == find(y);
}
bool merge(int x, int y) {
x = find(x);
y = find(y);
if (x == y) {
return false;
}
siz[x] += y;
f[y] = x;
return true;
}
int size(int x) {
return siz[find(x)];
}
};
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N;
std::cin >> N;
DSU f(10010);
std::vector<int> vis(10010);
for (int i = 0; i < N; i++) {
int k, v;
std::cin >> k >> v;
vis[v] = true;
int pa = f.find(v);
for (int j = 1; j < k; j++) {
int x;
std::cin >> x;
vis[x] = true;
f.merge(x, pa);
}
}
int cnt = 0;
std::set<int> st;
for (int i = 1; i <= 10000; i++) {
if (!vis[i]) continue ;
else {
cnt++;
st.insert(f.find(i));
}
}
std::cout << cnt << " " << st.size() << "\n";
int Q;
std::cin >> Q;
while (Q--) {
int x, y;
std::cin >> x >> y;
if (f.same(x, y)) {
std::cout << "Y\n";
} else {
std::cout << "N\n";
}
}
return 0;
}
L2-025 分而治之
思路:
- 直接记录每个点的度(
deg
)情况即可,然后每当删除一个点就把与这些点相连的点的度数都减1
,最后判断是否其他的点的度数为0
即可,这样保证了度数为0
的点是孤立的
时间复杂度: O ( Q × N l o g N ) O(Q \times NlogN) O(Q×NlogN)
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N, M;
std::cin >> N >> M;
std::vector<int> deg(N);
std::vector<std::vector<int>> adj(N);
for (int i = 0; i < M; i++) {
int u, v;
std::cin >> u >> v;
u--, v--;
adj[u].push_back(v);
adj[v].push_back(u);
deg[u]++, deg[v]++;
}
int Q;
std::cin >> Q;
while (Q--) {
int Np;
std::cin >> Np;
auto tmp = deg;
std::set<int> a;
for (int i = 0; i < Np; i++) {
int x;
std::cin >> x;
x--;
a.insert(x);
for (int v : adj[x]) {
tmp[v]--;
}
}
bool ok = false;
for (int i = 0; i < N; i++) {
if (a.count(i)) continue ;
if (tmp[i] != 0) {
ok = true;
break;
}
}
std::cout << (ok ? "NO\n": "YES\n");
}
return 0;
}
L2-026 小字辈
思路:
- 两遍
dfs
,第一遍先记录族谱树的最大深度,第二遍找到最大深度的所有节点,即为辈分最小的节点
时间复杂度: O ( N ) O(N) O(N)
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N;
std::cin >> N;
std::vector<int> a(N + 1);
for (int i = 1; i <= N; i++) {
std::cin >> a[i];
}
int u = 0;
std::map<int, std::vector<int>> mp;
for (int i = 1; i <= N; i++) {
mp[a[i]].push_back(i);
if (a[i] == -1) {
u = i;
}
}
int cnt = 0;
auto dfs1 = [&](auto self, int x, int dep) -> void {
cnt = std::max(cnt, dep);
for (int v : mp[x]) {
self(self, v, dep + 1);
}
};
dfs1(dfs1, u, 1);
std::vector<int> ans;
auto dfs2 = [&](auto self, int x, int dep) -> void {
if (dep == cnt) {
ans.push_back(x);
return ;
}
for (int v : mp[x]) {
self(self, v, dep + 1);
}
};
dfs2(dfs2, u, 1);
std::cout << cnt << "\n";
for (int i = 0; i < ans.size(); i++) {
std::cout << ans[i] << " \n"[i == ans.size() - 1];
}
return 0;
}
L2-027 名人堂与代金券
思路:
- 直接模拟即可,利用
map
和set
处理所有的顺序
时间复杂度: O ( k l o g l o g n ) O(kloglogn) O(kloglogn)
struct node {
std::string s;
int w;
};
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N, G, K;
std::cin >> N >> G >> K;
std::vector<node> a(N);
for (int i = 0; i < N; i++) {
std::cin >> a[i].s >> a[i].w;
}
int ans = 0;
for (int i = 0; i < N; i++) {
if (a[i].w >= 60 && a[i].w < G) {
ans += 20;
} else if (a[i].w >= G && a[i].w <= 100) {
ans += 50;
}
}
std::map<int, std::set<std::string>, std::greater<int>> mp;
for (auto [x, y] : a) {
mp[y].insert(x);
}
std::cout << ans << "\n";
int tot = 0, lst = 0;
std::vector<std::pair<std::string, int>> res;
for (auto [x, y] : mp) {
tot += 1;
for (auto s : y) {
std::cout << tot + lst << " " << s << " " << x << "\n";
}
lst += y.size() - 1;
if (lst + tot >= K) break;
}
return 0;
}
L2-029 特立独行的幸福
思路:
- 模拟,用
set
记录一下是否依赖即可,然后记录幸福数的个数
时间复杂度: O ( n l o g x ) O(nlogx) O(nlogx)
bool isprime(int x) {
if (x <= 1) return false;
for (int i = 2; i <= x / i; i++) {
if (x % i == 0) return false;
}
return true;
}
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int A, B;
std::cin >> A >> B;
std::vector<int> cnt(100010), vis(100010);
auto check = [&](int x) {
std::set<int> st;
int t = x;
while (t != 1) {
st.insert(t);
int sum = 0;
while (t) {
int v = t % 10;
t /= 10;
sum += v * v;
}
cnt[x]++;
vis[sum] = 1;
t = sum;
if (st.count(t)) return false;
}
return true;
};
std::vector<int> ans;
for (int i = A; i <= B; i++) {
if (check(i)) ans.push_back(i);
}
bool ok = false;
for (int i = 0; i < ans.size(); i++) {
if (isprime(ans[i])) cnt[ans[i]] <<= 1;
if (!vis[ans[i]]) {
std::cout << ans[i] << " " << cnt[ans[i]] << "\n";
ok = true;
}
}
if (!ok) {
std::cout << "SAD\n";
}
return 0;
}
L2-030 冰岛人
思路:
- 利用
map
来存储维京男性和维京女性以及普通男性和普通女性的姓名 - 然后利用暴力搜索出五代以内是否有公共祖先
时间复杂度: O ( N × l o g N ) O(N \times logN) O(N×logN)
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N;
std::cin >> N;
std::map<std::string, std::pair<int, std::string>> Record;
for (int i = 0; i < N; i++) {
std::string fname, sname;
std::cin >> fname >> sname;
if (sname.back() == 'n') { // 维京男性
Record[fname] = {1, sname.substr(0, sname.size() - 4)};
} else if (sname.back() == 'r') { // 维京女性
Record[fname] = {0, sname.substr(0, sname.size() - 7)};
} else if (sname.back() == 'm') { // 普通男性
Record[fname].first = 1;
} else { // 普通女性
Record[fname].first = 0;
}
}
auto check = [&](std::string a, std::string b) {
int cnt1 = 0, cnt2;
while (a != "") {
cnt2 = 0;
std::string b2 = b;
while (b2 != "") {
if (a == b2 && (cnt1 < 4 || cnt2 < 4)) return "No\n";
if (cnt1 >= 4 && cnt2 >= 4) return "Yes\n";
b2 = Record[b2].second;
cnt2++;
}
a = Record[a].second;
cnt1++;
}
return "Yes\n";
};
int M;
std::cin >> M;
while (M--) {
std::string fname, temp, sname;
std::cin >> fname >> temp >> sname >> temp;
if (!Record.count(fname) || !Record.count(sname)) {
std::cout << "NA\n";
} else if (Record[fname].first == Record[sname].first) {
std::cout << "Whatever\n";
} else {
std::cout << check(fname, sname);
}
}
return 0;
}
L2-031 深入虎穴
思路:
- 首先找到那个唯一入口门的位置,然后进行
dfs
即可,可以类比一下L2-026 小字辈这题,几乎一模一样,唯一的区别就是有向树找根,有向树的根的入度为0
.
时间复杂度: O ( N ) O(N) O(N)
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N;
std::cin >> N;
std::vector<int> deg(N);
std::vector<std::vector<int>> adj(N);
for (int i = 0; i < N; i++) {
int K;
std::cin >> K;
for (int j = 0; j < K; j++) {
int x;
std::cin >> x;
x--;
adj[i].push_back(x);
deg[x]++;
}
}
int u = -1;
for (int i = 0; i < N; i++) {
if (!deg[i]) u = i;
}
int mx = 1;
auto dfs1 = [&](auto self, int x, int dep) -> void {
mx = std::max(mx, dep);
for (int v : adj[x]) {
self(self, v, dep + 1);
}
};
dfs1(dfs1, u, 1);
auto dfs2 = [&](auto self, int x, int dep) -> void {
if (dep == mx) {
std::cout << x + 1 << "\n";
return ;
}
for (int v : adj[x]) {
self(self, v, dep + 1);
}
};
dfs2(dfs2, u, 1);
return 0;
}
L2-032 彩虹瓶
思路:
- 按照题目意思模拟,可以看出来就是考察的
stack
这个数据结构,只要发现存储的个数大于M
或者无法按顺序排列,就不行
时间复杂度: O ( K N ) O(KN) O(KN)
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N, M, K;
std::cin >> N >> M >> K;
for (int i = 0; i < K; i++) {
std::vector<int> a(N);
for (int j = 0; j < N; j++) {
std::cin >> a[j];
}
std::stack<int> stk;
std::vector<int> ans;
int tot = 1, f = 0;
for (int j = 0; j < N; j++) {
if (a[j] == tot) {
ans.push_back(tot);
tot++;
} else if (stk.size() && stk.top() == tot) {
stk.pop();
ans.push_back(tot);
tot++;
j--;
} else {
stk.push(a[j]);
}
if (stk.size() > M) f = true;
}
if (stk.size() > M || f) {
std::cout << "NO\n";
continue ;
}
while (stk.size()) {
ans.push_back(stk.top());
stk.pop();
}
if (std::is_sorted(ans.begin(), ans.end())) {
std::cout << "YES\n";
} else {
std::cout << "NO\n";
}
}
return 0;
}
L2-033 简单计算器
思路:
- 利用栈按照题意模拟
时间复杂度: O ( n ) O(n) O(n)
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N;
std::cin >> N;
std::stack<int> num;
for (int i = 0; i < N; i++) {
int x;
std::cin >> x;
num.push(x);
}
std::stack<char> op;
for (int i = 0; i + 1 < N; i++) {
char c;
std::cin >> c;
op.push(c);
}
auto check = [&](int x, int y, char op) {
if (op == '/' && y == 0) {
return false;
}
return true;
};
auto cal = [&](int x, int y, char op) {
if (op == '/') {
return x / y;
}
if (op == '*') {
return x * y;
}
if (op == '-') {
return x - y;
}
if (op == '+') {
return x + y;
}
};
while (num.size() > 1) {
int y = num.top();
num.pop();
int x = num.top();
num.pop();
char c = op.top();
op.pop();
if (check(x, y, c)) {
num.push(cal(x, y, c));
} else {
std::cout << "ERROR: " << x << "/0" << "\n";
return 0;
}
if (num.size() == 1) {
std::cout << num.top() << "\n";
break;
}
}
return 0;
}
L2-034 口罩发放
思路:
- 首先我们创建一个结构体
node
来存储每个人的个人有关信息,然后创建一个map
来存储最后一次的记录的日期,通过check
函数判断是否合法,然后按照题意模拟
时间复杂度: O ( D × T l o g T ) O(D \times TlogT) O(D×TlogT)
struct node {
std::string name, id, time;
int ill, pos;
};
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int D, P;
std::cin >> D >> P;
std::vector<node> ill;
std::map<std::string, int> mp;
std::set<std::string> gotten;
for (int t = 0; t < D; t++) {
int T, S;
std::cin >> T >> S;
auto check = [&](std::string x) {
if (x.size() != 18) return false;
for (char c : x) {
if (c < '0' || c > '9') return false;
}
return true;
};
std::vector<node> Record(T);
for (int i = 0; i < T; i++) {
std::cin >> Record[i].name >> Record[i].id >> Record[i].ill >> Record[i].time;
Record[i].pos = i;
if (!check(Record[i].id)) Record[i].name = "";
else {
if (!mp.count(Record[i].id)) mp[Record[i].id] = -30;
if (Record[i].ill && !gotten.count(Record[i].id)) {
ill.push_back(Record[i]);
gotten.insert(Record[i].id);
}
}
}
std::sort(Record.begin(), Record.end(), [&](auto i, auto j) {
if (i.time != j.time) return i.time < j.time;
return i.pos < j.pos;
});
int sum = 0;
for (int i = 0; i < T && sum < S; i++) {
if (Record[i].name != "" && t - mp[Record[i].id] > P) {
mp[Record[i].id] = t;
sum++;
std::cout << Record[i].name << " " << Record[i].id << "\n";
}
}
}
for (auto e : ill) {
std::cout << e.name << " " << e.id << "\n";
}
return 0;
}
L2-035 完全二叉树的层序遍历
思路:
- 可以发现,题目说这是一颗完全二叉树,那么可以知道,按照完全二叉树的遍历原理进行后序遍历,先进入左右子树(编号为
index * 2
和index * 2 + 1
,即index
右移1
位,和index
右移1
位+1
),cnt
为后序遍历的位置标记,并将当前所在的后序遍历的元素,填入ans[index]
内即可
时间复杂度: O ( N ) O(N) O(N)
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N;
std::cin >> N;
std::vector<int> a(N);
for (int i = 0; i < N; i++) {
std::cin >> a[i];
}
int tot = 0;
std::vector<int> ans(N + 1);
std::function<void(int)> dfs = [&](int x) {
if (x > N) return ;
dfs(x << 1);
dfs(x << 1 | 1);
ans[x] = a[tot++];
};
dfs(1);
for (int i = 1; i <= N; i++) {
std::cout << ans[i] << " \n"[i == N];
}
return 0;
}
L2-037 包装机
思路:
- 按照题目意思模拟,那个框其实就是一个栈,夹取物品就是出栈,按照意思模拟即可,只需要注意当队列为空,就不采取任何措施
时间复杂度: O ( n m ) O(nm) O(nm)
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N, M, S;
std::cin >> N >> M >> S;
std::map<int, std::deque<char>> mp;
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
char c;
std::cin >> c;
mp[i + 1].push_back(c);
}
}
int x;
std::string ans = "";
std::stack<char> stk;
while (std::cin >> x && x != -1) {
if (x != 0) {
if (mp[x].empty()) continue ;
if (stk.size() < S) {
if (mp[x].size()) {
stk.push(mp[x].front());
mp[x].pop_front();
}
} else {
char t = stk.top();
stk.pop();
ans += t;
if (mp[x].size()) {
stk.push(mp[x].front());
mp[x].pop_front();
}
}
} else {
if (stk.size()) {
char t = stk.top();
ans += t;
stk.pop();
}
}
}
std::cout << ans << "\n";
return 0;
}
L2-038 病毒溯源
思路:
- 其实也可以用
dfs
,求出深度最大的点即可,然后倒着找path
时间复杂度: O ( n k ) O(nk) O(nk)
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N;
std::cin >> N;
std::vector<int> deg(N), path(N);
std::vector<std::vector<int>> adj(N);
for (int i = 0; i < N; i++) {
int k;
std::cin >> k;
for (int j = 0; j < k; j++) {
int x;
std::cin >> x;
deg[x]++;
path[x] = i;
adj[i].push_back(x);
}
std::sort(adj[i].begin(), adj[i].end());
}
int u = 0;
for (int i = 0; i < N; i++) {
if (!deg[i]) u = i;
}
int ans = 0, mx = 0;
// bfs or dfs
// bfs version
std::queue<std::pair<int, int>> q;
q.push({u, 1});
while (!q.empty()) {
auto [x, dep] = q.front();
q.pop();
if (dep > mx) {
mx = dep;
ans = x;
}
for (int e : adj[x]) {
q.push({e, dep + 1});
}
}
/* dfs version
auto dfs = [&](auto self, int u, int dep) -> void {
if (dep > mx) {
mx = dep;
ans = u;
}
for (auto x : adj[u]) {
self(self, x, dep + 1);
}
};
dfs(dfs, u, 1);
*/
std::cout << mx << "\n";
std::vector<int> p;
while (ans != u) {
p.push_back(ans);
ans = path[ans];
}
p.push_back(u);
for (int i = 0; i < p.size(); i++) {
std::cout << p[p.size() - i - 1] << " \n"[i == p.size() - 1];
}
return 0;
}
L2-039 清点代码库
思路:
- 就是计算有多少个序列是重复的,直接用
map
维护即可
时间复杂度: O ( n ( l o g n + m ) ) O(n(logn + m)) O(n(logn+m))
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N, M;
std::cin >> N >> M;
std::map<std::vector<int>, int> mp;
std::vector<std::vector<int>> a(N, std::vector<int>(M));
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
std::cin >> a[i][j];
}
mp[a[i]]++;
}
std::vector<std::pair<int, std::vector<int>>> ans;
for (auto [x, y] : mp) {
ans.push_back({y, x});
}
std::cout << ans.size() << "\n";
std::sort(ans.begin(), ans.end(), [&](auto i, auto j) {
if (i.first != j.first) {
return i.first > j.first;
}
return i.second < j.second;
});
for (auto [x, y] : ans) {
std::cout << x << " ";
for (int i = 0; i < y.size(); i++) {
std::cout << y[i] << " \n"[i == y.size() - 1];
}
}
return 0;
}
L2-040 哲哲打游戏
思路:
- 模拟即可,做一个结构体来存每次的存档情况,然后按照题目意思模拟
时间复杂度: O ( n k ) O(nk) O(nk)
struct node {
int pos;
std::vector<int> path;
};
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N, M;
std::cin >> N >> M;
std::vector<std::vector<int>> adj(N + 1);
for (int i = 0; i < N; i++) {
int K;
std::cin >> K;
for (int j = 0; j < K; j++) {
int x;
std::cin >> x;
adj[i + 1].push_back(x);
}
}
int now = 1;
std::vector<int> tmp(1, 1);
std::vector<node> save(105);
for (int i = 0; i < M; i++) {
int op, x;
std::cin >> op >> x;
if (op == 0) {
tmp.push_back(adj[now][x - 1]);
now = adj[now][x - 1];
} else if (op == 1) {
save[x].pos = now;
save[x].path = tmp;
std::cout << now << "\n";
} else {
now = save[x].pos;
tmp = save[x].path;
}
}
std::cout << tmp.back() << "\n";
return 0;
}
L2-041 插松枝
思路:
tree[N]
代表每一个松针数组,bot
代表盒子,我们需要按照题目的意思模拟- 但需要特别注意的是,最后一定要把盒子清空,结束的时候,盒子里不能有任何松针
时间复杂度: O ( N ) O(N) O(N)
constexpr int N = 1010;
std::vector<int> tree[N];
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N, M, K;
std::cin >> N >> M >> K;
std::vector<int> a(N);
for (int i = 0; i < N; i++) {
std::cin >> a[i];
}
int tot = 1;
std::deque<int> bot;
for (int i = 0; i < N; i++) {
if (tree[tot].size() == K) {
tot++;
}
if (bot.empty()) {
if (tree[tot].empty()) {
tree[tot].push_back(a[i]);
} else if (tree[tot].back() >= a[i]) {
tree[tot].push_back(a[i]);
} else {
bot.push_back(a[i]);
}
} else if (bot.size() < M) {
if (tree[tot].empty()) {
int tmp = bot.back();
bot.pop_back();
tree[tot].push_back(tmp);
i--;
} else if (bot.back() <= tree[tot].back()) {
int tmp = bot.back();
bot.pop_back();
tree[tot].push_back(tmp);
i--;
} else if (bot.back() > tree[tot].back()) {
if (a[i] <= tree[tot].back()) {
tree[tot].push_back(a[i]);
} else {
bot.push_back(a[i]);
}
}
} else if (bot.size() == M) {
if (tree[tot].empty()) {
int tmp = bot.back();
bot.pop_back();
tree[tot].push_back(tmp);
i--;
} else if (bot.back() <= tree[tot].back()) {
int tmp = bot.back();
bot.pop_back();
tree[tot].push_back(tmp);
i--;
} else if (bot.back() > tree[tot].back()) {
if (a[i] <= tree[tot].back()) {
tree[tot].push_back(a[i]);
} else {
tot++;
i--;
}
}
}
}
while (bot.size()) {
if (tree[tot].empty() || bot.back() <= tree[tot].back()) {
tree[tot].push_back(bot.back());
bot.pop_back();
} else {
tot++;
}
if (tree[tot].size() == K) tot++;
}
for (int i = 1; i <= tot; i++) {
if (tree[i].size()) {
for (int j = 0; j < tree[i].size(); j++) {
std::cout << tree[i][j] << " \n"[j == tree[i].size() - 1];
}
}
}
return 0;
}
L2-042 老板的作息表
思路:
- 可以根据题意,设置起始时间
00:00:00
和终止时间23:59:59
,然后将题目所给的时间排序,然后开始匹配,当找到头部相等,就更新头部信息 → \rightarrow → 当前位置的结束时间,最后如果结束时间不等于这一天的最后时刻,就直接输出最后的时间到最后的时刻
时间复杂度: O ( N ) O(N) O(N)
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N;
std::cin >> N;
std::string c;
std::vector<std::pair<std::string, std::string>> a(N);
for (int i = 0; i < N; i++) {
std::cin >> a[i].first >> c >> a[i].second;
}
std::sort(a.begin(), a.end(), [&](auto i, auto j) {
return i.first < j.first;
});
std::string start = "00:00:00", end = "23:59:59";
for (int i = 0; i < N; i++) {
if (a[i].first == start) {
start = a[i].second;
} else {
std::cout << start << " - " << a[i].first << "\n";
start = a[i].second;
}
}
if (start != end) {
std::cout << start << " - " << end << "\n";
}
return 0;
}
L2-043 龙龙送外卖
思路:
- 快递站是一颗树,我们求每一个节点到
root
节点的深度,然后当增加一个外卖站点(节点),我们需要走的距离为计算从 x x x 到 x x x 所在的最远祖先(第一个被访问过的祖先)的路径长度 Σ i = 0 M n o d e n e w − n o d e l a s t − m a x ( n o d e i ) \Sigma_{i=0}^{M}node_{new} - node_{last} - max(node_{i}) Σi=0Mnodenew−nodelast−max(nodei)
时间复杂度: O ( N + M × D e p t h t r e e ) O(N + M \times Depth_{tree}) O(N+M×Depthtree)
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N, M;
std::cin >> N >> M;
std::vector<int> p(N + 1);
std::vector<std::vector<int>> adj(N + 1);
for (int i = 1; i <= N; i++) {
int x;
std::cin >> x;
if (x < 0) {
p[0] = i;
} else {
adj[x].push_back(i);
}
p[i] = x;
}
// bfs or dfs
std::vector<int> vis(N + 1), dist(N + 1);
vis[p[0]] = 1;
// bfs
std::queue<std::pair<int, int>> q;
q.push({p[0], 0});
while (!q.empty()) {
auto [now, dep] = q.front();
q.pop();
dist[now] = dep;
for (int x : adj[now]) {
q.push({x, dep + 1});
}
}
// dfs
auto dfs = [&](auto self, int u, int dep) -> void {
dist[u] = dep;
for (auto x : adj[u]) {
self(self, x, dep + 1);
}
};
dfs(dfs, p[0], 0);
int res = 0, lst = 0, max = 0;
for (int i = 0; i < M; i++) {
int x;
std::cin >> x;
lst = x;
while (!vis[lst]) { // 访问所有点了的外卖至少一次
vis[lst] = 1;
lst = p[lst];
}
res += dist[x] - dist[lst];
max = std::max(max, dist[x]);
std::cout << res * 2 - max << "\n";
}
return 0;
}
L2-044 大众情人
思路:
- 我们设
2
个集合,分别存男性和女性编号,然后用dist[i][Fri]
来存储当前这个人i
到朋友Fri
之间的距离,然后可以使用Floyd
最短路来跑一遍,来计算每个人之间的最短距离 - 由于题目中的“异性缘”的定义规则为:我们记一个人 i i i 在一个异性 j j j 眼中的距离感为 D i j D_{ij} Dij,将 i i i 的“异性缘”定义为 1 / m a x j ∈ S ( i ) { D i j } 1/max_{j∈S(i)}\{D_{ij}\} 1/maxj∈S(i){Dij},其中 S ( i ) S(i) S(i) 是相对于 i i i 的所有异性的集合。
- 所以我们可以直接暴力枚举两个集合,然后求出当前异性缘最大的那个人的编号
时间复杂度: O ( N 3 ) O(N^3) O(N3)
constexpr int inf = 1E9;
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N;
std::cin >> N;
std::vector<int> male, female;
std::vector<std::vector<int>> dist(N + 1, std::vector<int>(N + 1, inf));
for (int i = 1; i <= N; i++) {
int K;
char C;
std::cin >> C >> K;
if (C == 'F') {
female.push_back(i);
} else {
male.push_back(i);
}
for (int j = 0; j < K; j++) {
int Fri, d;
std::cin >> Fri >> C >> d;
dist[i][Fri] = d;
}
}
for (int k = 1; k <= N; k++) {
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= N; j++) {
dist[i][j] = std::min(dist[i][k] + dist[k][j], dist[i][j]);
}
}
}
auto get = [&](auto &find, auto &base, auto &target, int mark = 0) {
for (int i = 0, score = 0; i < find.size(); i++, score = 0) {
for (int j = 0; j < base.size(); j++) {
score = std::max(score, dist[base[j]][find[i]]);
}
target[score].insert(find[i]);
}
for (auto x : target.begin()->second) {
if (mark++) std::cout << " ";
std::cout << x;
}
};
std::map<int, std::set<int>> Fans, Mans;
get(female, male, Fans);
std::cout << "\n";
get(male, female, Mans);
return 0;
}
L2-045 堆宝塔
思路:
- 用栈模拟圈的上下移动即可
时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)
constexpr int N = 10010;
std::vector<int> v[N];
int a[N];
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n;
std::cin >> n;
for (int i = 0; i < n; i++) {
std::cin >> a[i];
}
std::stack<int> s;
int p = 0;
v[p].push_back(a[0]);
for (int i = 1; i < n; i++) {
if (v[p].size() && a[i] < v[p].back()) { // 如果比下面的那个圈小
v[p].push_back(a[i]);
} else if (v[p].size() && a[i] >= v[p].back()) { // 如果比下面那个圈大,
if (s.empty() || (s.size() && a[i] > s.top())) s.push(a[i]); // 如果B柱子为空
else if (s.size() && a[i] <= s.top()) {
p++;
while (s.size() && s.top() > a[i]) {
v[p].push_back(s.top());
s.pop();
}
v[p].push_back(a[i]);
}
}
}
if (s.size()) p++;
std::cout << p + 1 << ' ';
int mx = s.size();
for (int i = 0; i <= p; i++) {
mx = std::max(mx, (int)v[i].size());
}
std::cout << mx;
return 0;
}
L2-046 天梯赛的赛场安排
思路:
- 用
BFS
模拟,在优先队列(大根堆)里,先分配数量多的,然后分配数量少的
时间复杂度: O ( N l o g N ) O(NlogN) O(NlogN)
constexpr int N = 250010;
int room[N];
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N, C;
std::cin >> N >> C;
std::vector<std::string> s(N);
std::priority_queue<std::pair<int, std::string>> q;
for (int i = 0; i < N; i++) {
int x;
std::cin >> s[i] >> x;
q.push({x, s[i]});
}
int tot = 0, ok = 0;
std::map<std::string, int> mp;
while (q.size()) {
auto [cur, t] = q.top();
q.pop();
mp[t]++;
if (cur >= C) {
room[++tot] = 0;
if (cur != C) {
q.push({cur - C, t});
}
} else {
ok = false;
for (int i = 1; i <= tot; i++) {
if (room[i] >= cur) {
room[i] -= cur;
ok = true;
break;
}
}
if (!ok) {
room[++tot] = C - cur;
}
}
}
int sum = 0;
for (int i = 0; i < N; i++) {
std::cout << s[i] << " " << mp[s[i]] << "\n";
}
std::cout << tot << "\n";
return 0;
}
L2-047 锦标赛
思路:
时间复杂度: O ( k × 2 k ) O(k \times 2^k) O(k×2k)
constexpr int N = 50, M = 1 << 20;
int id[N][M];
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int k;
std::cin >> k;
bool ok = true;
std::vector<int> ans(M);
std::vector<std::vector<int>> a(k + 1);
for (int i = 1; i <= k; i++) {
int n = 1 << (k - i);
a[i].resize(n);
for (int j = 0; j < n; j++) {
std::cin >> a[i][j];
if (i == 1) {
ans[j << 1] = a[i][j];
id[i][j] = (j << 1 | 1);
continue ;
}
int max = std::max(a[i][j], std::max(a[i - 1][j << 1], a[i - 1][j << 1 | 1]));
if (a[i][j] < a[i - 1][j << 1] && a[i][j] < a[i - 1][j << 1 | 1]) {
ok = false;
break;
} else if (a[i][j] >= a[i - 1][j << 1]) {
ans[id[i - 1][j << 1]] = a[i][j];
id[i][j] = id[i - 1][j << 1 | 1];
} else {
ans[id[i - 1][j << 1 | 1]] = a[i][j];
id[i][j] = id[i - 1][j << 1];
}
a[i][j] = max;
}
}
int w;
std::cin >> w;
if (a[k][0] > w) {
ok = false;
} else {
ans[id[k][0]] = w;
}
if (!ok) {
std::cout << "No Solution\n";
} else {
for (int i = 0; i < (1 << k); i++) {
std::cout << ans[i] << " \n"[i == (1 << k)];
}
}
return 0;
}
L2-048 寻宝图
思路:
- 直接
BFS
搜一下即可
时间复杂度: O ( n × m ) O(n \times m) O(n×m)
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n, m;
std::cin >> n >> m;
std::vector<std::vector<char>> g(n, std::vector<char> (m, 0));
std::vector<std::vector<int>> st(n, std::vector<int> (m, 0));
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
std::cin >> g[i][j];
}
}
bool flag = false;
const int dx[4] = {0, 0, -1, 1}, dy[4] = {-1, 1, 0, 0};
auto bfs = [&] (int x, int y) {
std::queue<std::pair<int, int>> q;
q.push({x, y});
st[x][y] = 1;
while (q.size()) {
auto t = q.front();
q.pop();
if (g[t.first][t.second] >= '2' && g[t.first][t.second] <= '9') flag = true;
for (int i = 0; i < 4; i++) {
int a = dx[i] + t.first, b = dy[i] + t.second;
if (a < 0 || b < 0 || a >= n || b >= m || st[a][b]) continue;
if (g[a][b] == '0') continue;
st[a][b] = 1;
q.push({a, b});
}
}
};
int ans = 0, res = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (!st[i][j] && g[i][j] != '0') {
bfs(i, j);
ans++;
if (flag) {
res++;
}
flag = false;
}
}
}
std::cout << ans << ' ' << res << '\n';
return 0;
}
L2-049 鱼与熊掌
思路:
- 直接开
m
个set
即可,假设x
和y
是询问的两个物品,那么我们只需要枚举集合x
中的人员,与y
中的相同的数量即可
时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n, m;
std::cin >> n >> m;
std::set<int> st[m + 1];
for (int i = 0; i < n; i++) {
int k;
std::cin >> k;
for (int j = 0; j < k; j++) {
int x;
std::cin >> x;
st[x].insert(i);
}
}
int Q;
std::cin >> Q;
while (Q--) {
int x, y;
std::cin >> x >> y;
int ans = 0;
for (auto v : st[x]) {
if (st[y].count(v)) {
ans++;
}
}
std::cout << ans << "\n";
}
return 0;
}
L2-050 懂蛇语
思路:
- 如果按照题目意思模拟,不考虑一句话的前面是否存在空格,就会在第二个点出现格式错误,所以我们要先将前面的空格去除,然后存储的时候,需要把前面删除的空格给补回来
时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)
signed main() {
std::cin.tie(nullptr);
int N;
std::cin >> N;
getchar();
std::map<std::string, std::vector<std::string>> mp;
for (int i = 0; i < N; i++) {
std::string s;
std::getline(std::cin, s);
int l = 0;
while (s[l] == ' ') {
l++;
}
s = s.substr(l);
std::vector<std::string> vec;
for (int j = 0; j < s.size(); j++) {
int k = j, len = 0;
while (k < s.size() && s[k] != ' ') {
k++;
len++;
}
while (s[k] == ' ') {
k++;
}
std::string t = s.substr(j, len);
vec.push_back(t);
j = k - 1;
}
std::string tmp;
for (int j = 0; j < vec.size(); j++) {
tmp += vec[j][0];
}
if (l != 0) {
while (l--) {
s = ' ' + s;
}
}
mp[tmp].push_back(s);
}
int M;
std::cin >> M;
getchar();
for (int i = 0; i < M; i++) {
std::string s;
std::getline(std::cin, s);
int l = 0;
while (s[l] == ' ') {
l++;
}
s = s.substr(l);
std::vector<std::string> vec;
for (int j = 0; j < s.size(); j++) {
int k = j, len = 0;
while (k < s.size() && s[k] != ' ') {
k++;
len++;
}
while (s[k] == ' ') {
k++;
}
std::string t = s.substr(j, len);
vec.push_back(t);
j = k - 1;
}
std::string tmp;
for (int j = 0; j < vec.size(); j++) {
tmp += vec[j][0];
}
if (!mp.count(tmp)) {
std::cout << s << "\n";
} else {
if (mp[tmp].size() == 1) {
std::cout << mp[tmp][0] << "\n";
} else {
int tot = 0;
std::sort(mp[tmp].begin(), mp[tmp].end());
for (auto x : mp[tmp]) {
if (++tot != mp[tmp].size()) {
std::cout << x << "|";
} else {
std::cout << x << "\n";
}
}
}
}
}
return 0;
}
L2-051 满树的遍历
思路:
- 只需要知道一颗树怎么存以及树的前序遍历即可,没啥难的地方
时间复杂度: O ( n ) O(n) O(n)
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n;
std::cin >> n;
int root = 0;
std::vector<int> deg(n + 1);
std::vector<std::vector<int>> adj(n + 1);
for (int i = 1; i <= n; i++) {
int x;
std::cin >> x;
if (x == 0) {
root = i;
}
adj[x].push_back(i);
deg[x]++;
}
std::set<int> st;
for (int i = 1; i <= n; i++) {
st.insert(deg[i]);
}
if (st.size() == 2 || n == 1) {
std::cout << *--st.end() << " " << "yes\n";
} else {
std::cout << *--st.end() << " " << "no\n";
}
int tot = 0;
auto dfs = [&](auto self, int u) -> void {
std::cout << u << " \n"[++tot == n];
for (int x : adj[u]) {
self(self, x);
}
};
dfs(dfs, root);
return 0;
}
L2-052 吉利矩阵
思路:
- 我们将问题改变为存储每一行每一列的值,其中
row[i]
和col[i]
分别表示第i
行与第i
列的和,然后我们开始暴搜
时间复杂度: O ( N L ) O(N^L) O(NL)
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int L, N;
std::cin >> L >> N;
int ans = 0;
std::vector<int> row(N + 1), col(N + 1);
auto dfs = [&](auto self, int x, int y) {
if (x == N + 1) {
ans++;
return ;
}
for (int i = 0; i <= L; i++) {
row[x] += i;
col[y] += i;
if (row[x] <= L && col[y] <= L) {
int v = 0; // 剪枝优化
if (x == N) {
if (col[y] == L) {
v++;
}
} else {
v++;
}
if (y == N) {
if (row[x] == L) {
v++;
}
} else {
v++;
}
if (v == 2) {
self(self, x + (y / N), y % N + 1);
}
}
col[y] -= i;
row[x] -= i;
}
};
dfs(dfs, 1, 1);
std::cout << ans << "\n";
return 0;
}