2025寒假天梯赛训练2

2025寒假天梯赛训练2

L1-1 谢谢卡尔!

思路

谢谢卡尔!(>_<)/

代码

谢谢卡尔!\(>_<)/

L1-2 现在是,幻想时间!

思路

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int N, T;
	cin >> N >> T;

	cout << fixed << setprecision(3) << 1.0 * N / T << "\n";

	return 0;
}

L1-3 你是来陪可莉炸鱼的吗?

思路

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int n;
	cin >> n;

	int ans = 0;
	while (n--) {
		int x;
		cin >> x;

		if (x < 100) {
			ans += 1;
		} else if (x < 200) {
			ans += 2;
		} else if (x < 300) {
			ans += 5;
		} else if (x < 400) {
			ans += 10;
		} else {
			ans += 15;
		}
	}

	cout << ans << "\n";

	return 0;
}

L1-4 扫雷游戏

思路

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int n, m;
	cin >> n >> m;

	vector<string> s(n);
	for (auto &i : s) {
		cin >> i;
	}

	const int u[] = {1, 1, 1, -1, -1, -1, 0, 0};
	const int v[] = {1, 0, -1, 1, 0, -1, 1, -1};

	for (int i = 0; i < n; i ++) {
		for (int j = 0; j < m; j ++) {
			int res = 0;
			for (int k = 0; k < 8; k ++) {
				int x = i + u[k], y = j + v[k];
				if (x >= 0 && x < n && y >= 0 && y < m) {
					res += s[x][y] == '*';
				}
			}
			if (s[i][j] == '*') {
				cout << "*";
			} else if (res == 0) {
				cout << ".";
			} else {
				cout << res;
			}
			if (j == m - 1) {
				cout << "\n";
			}
		}
	}

	return 0;
}

L1-5 史莱姆

思路

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int n;
	cin >> n;

	string s;
	cin >> s;

	string ans = "";
	for (int i = 0; i < n; i ++) {
		if (i == 0 || s[i] != s[i - 1]) {
			ans += s[i];
		}
	}

	cout << ans.size() << "\n";
	cout << ans << "\n";

	return 0;
}

L1-6 加密通信

思路

注意减法取模时可能有 \(n \le m\) ,所以需要乘以一个 \(m\)

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int n, m;
	cin >> n >> m ;

	string s;
	cin >> s;

	vector<int> a(26);
	for (int i = 0; i < 26; i ++) {
		cin >> a[i];
	}

	for (int i = 0; i < n; i ++) {
		cout << a[s[(i - m + n * m) % n] - 'a'];
	}

	return 0;
}

L1-7 字符操作

思路

记录该字符串是否处于前后缀交换期间,若是,则对于 \(1\) 操作的位置需要右移一半。

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int n;
	cin >> n;

	string s;
	cin >> s;

	int q;
	cin >> q;

	bool ok = 0;
	while (q--) {
		int op, a, b;
		cin >> op >> a >> b;

		a--, b--;
		if (op == 1) {
			if (!ok) {
				swap(s[a], s[b]);
			} else {
				swap(s[(a + n) % (2 * n)], s[(b + n) % (2 * n)]);
			}
		} else {
			ok ^= 1;
		}
	}

	if (ok) {
		s = s.substr(n) + s.substr(0, n);
	}

	cout << s << "\n";

	return 0;
}

L1-8 vivo50!

思路

模拟。

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int n, k;
	cin >> n >> k;

	vector<pair<double, string>> grade;
	for (int i = 0; i < n; i ++) {
		string s;
		double f1;
		int f2, f3, gua;
		cin >> s >> f1 >> f2 >> f3 >> gua;
		if (!gua) continue;

		f1 = (f1 * 10) + 50;
		f2 += 70;
		f2 = min(f2, 100);
		grade.emplace_back((f1 + f3) * 0.7 + f2 * 0.3, s);
	}

	sort(grade.begin(), grade.end(), [](auto x, auto y) {
		if (x.first == y.first) return x.second < y.second;
		return x.first > y.first;
	});

	int rank = 0;
	cout << fixed << setprecision(1);
	for (int i = 0; i < grade.size(); i ++) {
		rank = i + 1;
		if (rank > k) break;
		cout << rank << " " << grade[i].second << " " << grade[i].first << "\n";
		while (i + 1 < grade.size() && grade[i + 1].first == grade[i].first) {
			i ++;
			cout << rank << " " << grade[i].second << " " << grade[i].first << "\n";
		}
	}

	return 0;
}

L2-1 游戏圈

思路

并查集模板。

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

struct DSU {
	vector<int> rank, p;
	DSU(int n) {
		rank.assign(n, 0);
		p.assign(n, 0);
		iota(p.begin(), p.end(), 0);
	}
	void link(int x, int y) {
		if (x == y)
			return;
		if (rank[x] > rank[y])
			p[y] = x;
		else
			p[x] = y;
		if (rank[x] == rank[y])
			rank[y]++;
	}
	int find(int x) {
		return x == p[x] ? x : (p[x] = find(p[x]));
	}
	void merge(int x, int y) {
		link(find(x), find(y));
	}
	bool same(int x, int y) {
		return find(x) == find(y);
	}
};

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int n, m, q;
	cin >> n >> m >> q;

	DSU dsu(n + 1);
	while (m--) {
		int a, b;
		cin >> a >> b;
		dsu.merge(a, b);
	}

	while (q--) {
		int a, b;
		cin >> a >> b;
		cout << (dsu.same(a, b) ? "yes" : "no") << "\n";
	}

	int res = 0;
	for (int i = 1; i <= n; i ++) {
		res += dsu.find(i) == i;
	}

	cout << res << "\n";

	return 0;
}

L2-2 组套题

思路

比较坑的点是,题中并未对最接近说明,我以为是左右差值越小越接近,实际上是一直先找大的,大的没有了才找小的。

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int n;
	cin >> n;

	vector<int> t(10), has(10);
	for (int i = 0; i < 10; i ++) {
		cin >> t[i];
	}

	vector T(10, vector<array<int, 3>>());
	for (int i = 1; i <= n; i ++) {
		int k;
		cin >> k;

		while (k--) {
			char c;
			int hard, num;
			cin >> c >> hard >> c >> num;
			hard --;
			T[hard].push_back({hard, i, num});
			has[hard] ++;
		}
	}

	for (auto &x : T) {
		reverse(x.begin(), x.end());
	}

	vector<array<int, 3>> ans;
	auto del = [&](int i, int j)->void{
		while (t[i] && has[j]) {
			ans.push_back(T[j].back());
			T[j].pop_back();
			t[i] -- , has[j] --;
		}
	};

	for (int i = 0; i < 10; i ++) {
		del(i, i);
		if (t[i]) {
			for (int x = 1; x < 10; x ++) {
				if (i + x < 10 && has[i + x] && t[i]) {
					del(i, i + x);
				}
			}
			for (int x = 1; x < 10; x ++) {
				if (i - x >= 0 && has[i - x] && t[i]) {
					del(i, i - x);
				}
			}
		}
	}

	sort(ans.begin(), ans.end());

	for (int i = 0; i < ans.size(); i ++) {
		auto x = ans[i];
		cout << x[1] << '-' << x[2] << " \n"[i == ans.size() - 1];
	}

	return 0;
}

L2-3 简单的数数

思路

经典计数\(DP\)

\(dp_{i,j}\) 表示第 \(i\) 位最后合并成 \(j\) 的方案数,则有转移方程:

\[dp_{i,j + a_i \pmod{10}} += dp_{i-1,j} \]

\[dp_{i,j \times a_i \pmod{10}} += dp_{i-1,j} \]

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

//------取模机------//
using i64 = long long;
template<class T>
constexpr T power(T a, i64 b) {
	T res {1};
	for (; b; b /= 2, a *= a) {
		if (b % 2) {
			res *= a;
		}
	}
	return res;
} // 快速幂

constexpr i64 mul(i64 a, i64 b, i64 p) {
	i64 res = a * b - i64(1.L * a * b / p) * p;
	res %= p;
	if (res < 0) {
		res += p;
	}
	return res;
} // 取模乘

template<i64 P>
struct MInt {
	i64 x;
	constexpr MInt() : x {0} {}
	constexpr MInt(i64 x) : x {norm(x % getMod())} {}

	static i64 Mod;
	constexpr static i64 getMod() {
		if (P > 0) {
			return P;
		} else {
			return Mod;
		}
	}
	constexpr static void setMod(i64 Mod_) {
		Mod = Mod_;
	}//只有P<=0, setMod才生效
	constexpr i64 norm(i64 x) const {
		if (x < 0) {
			x += getMod();
		}
		if (x >= getMod()) {
			x -= getMod();
		}
		return x;
	}
	constexpr i64 val() const {
		return x;
	}
	constexpr MInt operator-() const {
		MInt res;
		res.x = norm(getMod() - x);
		return res;
	}
	constexpr MInt inv() const {
		return power(*this, getMod() - 2);
	}
	constexpr MInt &operator*=(MInt rhs) & {
		if (getMod() < (1ULL << 31)) {
			x = x * rhs.x % int(getMod());
		} else {
			x = mul(x, rhs.x, getMod());
		}
		return *this;
	}
	constexpr MInt &operator+=(MInt rhs) & {
		x = norm(x + rhs.x);
		return *this;
	}
	constexpr MInt &operator-=(MInt rhs) & {
		x = norm(x - rhs.x);
		return *this;
	}
	constexpr MInt &operator/=(MInt rhs) & {
		return *this *= rhs.inv();
	}
	friend constexpr MInt operator*(MInt lhs, MInt rhs) {
		MInt res = lhs;
		res *= rhs;
		return res;
	}
	friend constexpr MInt operator+(MInt lhs, MInt rhs) {
		MInt res = lhs;
		res += rhs;
		return res;
	}
	friend constexpr MInt operator-(MInt lhs, MInt rhs) {
		MInt res = lhs;
		res -= rhs;
		return res;
	}
	friend constexpr MInt operator/(MInt lhs, MInt rhs) {
		MInt res = lhs;
		res /= rhs;
		return res;
	}
	friend constexpr std::istream &operator>>(std::istream &is, MInt &a) {
		i64 v;
		is >> v;
		a = MInt(v);
		return is;
	}
	friend constexpr std::ostream &operator<<(std::ostream &os, const MInt &a) {
		return os << a.val();
	}
	friend constexpr bool operator==(MInt lhs, MInt rhs) {
		return lhs.val() == rhs.val();
	}
	friend constexpr bool operator!=(MInt lhs, MInt rhs) {
		return lhs.val() != rhs.val();
	}
	friend constexpr bool operator<(MInt lhs, MInt rhs) {
		return lhs.val() < rhs.val();
	}
};

constexpr int MOD[] = {998244353, 1000000007};
using Z = MInt<MOD[0]>;
//------取模机------//

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int n;
	cin >> n;

	vector<int> a(n + 1);
	for (int i = 1; i <= n; i ++) {
		cin >> a[i];
	}

	vector<Z> dp(10);
	dp[(a[1] + a[2]) % 10] += 1;
	dp[(a[1] * a[2]) % 10] += 1;
	for (int i = 3; i <= n; i ++) {
		vector<Z> ndp(10);
		for (int j = 0; j < 10; j ++) {
			ndp[(j + a[i]) % 10] += dp[j];
			ndp[(j * a[i]) % 10] += dp[j];
		}
		dp = ndp;
	}

	for (int i = 0; i < 10; i ++) {
		cout << dp[i] << "\n";
	}

	return 0;
}

L2-4 回家日

思路

最短路变式,其中维护最短改成取最解封时间大值即可。

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int n, m, k;
	cin >> n >> m >> k;

	vector<int> c(n + 1);
	for (int i = 1; i <= n; i ++) {
		int x;
		cin >> x;
		c[x] = i;
	}

	vector g(n + 1, vector<int>());

	for (int i = 0; i < m; i ++) {
		int x, y;
		cin >> x >> y;
		g[x].emplace_back(y);
		g[y].emplace_back(x);
	}

	vector<int> dis(n + 1);

	priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> Q;
	Q.emplace(c[k], k);

	while (Q.size()) {
		auto [d, u] = Q.top();
		Q.pop();

		if (dis[u]) continue;
		dis[u] = d;

		for (auto v : g[u]) {
			int nd = max(c[v], d);
			if (!dis[v] || nd < dis[v]) {
				Q.emplace(nd, v);
			}
		}
	}

	for (int i = 1; i <= n; i ++) {
		cout << dis[i] << " \n"[i == n];
	}

	return 0;
}

L3-1 忽远忽近的距离

思路

题目要求在小明的梦境中找到从起始幻境到目标幻境的最短路径,路径的权值由共享质因子决定。核心思路是将幻境和质因子抽象为图中的节点,通过堆优化Dijkstra算法求解最短路径。

对于每个幻境 \(a_i\),分解其所有质因子 \(z\),并创建虚拟节点 \(z + N\)\(N\) 是幻境编号上限)。幻境 \(a_i\) 到其质因子虚拟节点 \(z + N\) 的边权为 \(\frac{a_i}{z}\),反向边权为 \(\frac{a_i}{z}\)。这样,若两个幻境 \(x\)\(y\) 有共同质因子 \(z\),则可通过虚拟节点 \(z + N\) 连接,路径总权值为 \(\frac{x}{z} + \frac{y}{z} = \frac{x + y}{z}\)

使用堆优化的 Dijkstra 算法。维护每个节点的最小距离 \(dis\) 和前驱节点 \(prev\)。每次从队列中取出当前距离最小的节点,松弛其邻接边。

从终点回溯前驱节点,得到完整路径。过滤掉质因子虚拟节点,仅保留实际幻境编号。

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

constexpr int N = 1e6 + 10;
int prime[N], id[N];

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int n;
	cin >> n;

	vector<int> a(n + 1);
	for (int i = 1; i <= n; i ++) {
		cin >> a[i];
		id[a[i]] = 1;
	}

	using pdi = pair<int, int>;
	vector g(2 * N, vector<pdi>());
	for (int i = 2; i < N; i ++) {
		if (!prime[i]) {
			for (int j = i; j < N; j += i) {
				if (id[j]) {
					g[j].emplace_back(j / i, i + N);
					g[i + N].emplace_back(j / i, j);
				}
				if (j > i) {
					prime[j] = 1;
				}
			}
		}
	}

	int st, ed;
	cin >> st >> ed;
	if (st == ed) {
		cout << 0 << "\n" << st << "\n";
		return 0;
	}

	constexpr int INF = 1e9;
	vector<int> dis(2 * N, INF), prev(2 * N, -1);
	priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> Q;
	dis[st] = 0;
	Q.emplace(0, st);

	while (!Q.empty()) {
		auto [d, u] = Q.top();
		Q.pop();
		if (u == ed) break;
		if (d > dis[u]) continue;

		for (auto [w, v] : g[u]) {
			if (dis[v] > dis[u] + w) {
				dis[v] = dis[u] + w;
				prev[v] = u;
				Q.emplace(dis[v], v);
			}
		}
	}

	if (dis[ed] >= INF) {
		cout << "Wake up Xiaoming\n-1\n";
		return 0;
	}

	vector<int> path;
	for (int u = ed; u != -1; u = prev[u]) {
		path.push_back(u);
	}
	reverse(path.begin(), path.end());

	vector<int> rpath;
	for (int x : path) {
		if (x < N) {
			rpath.push_back(x);
		}
	}

	cout << dis[ed] << '\n';
	for (int i = 0; i < rpath.size(); ++i) {
		cout << rpath[i] << " \n"[i == rpath.size() - 1] ;
	}

	return 0;
}

思路

代码


posted @ 2025-01-26 23:15  Ke_scholar  阅读(18)  评论(0)    收藏  举报