A 吝啬的拒绝

p个顶点,m条边的无向带权图,给出n个点(可重复), 求这n个点到图中某个顶点的距离和的最小值。

由于 ,我们可以使用 求任意点之间的最短距离,时间复杂度

然后我们枚举每个顶点,计算距离和求 即可。总时间复杂度

更新: 理论上复杂度是不够的,所以改用 来求任意点之间的最短距离。总时间复杂度

#include <bits/stdc++.h>
using namespace std;

// 2023 OneWan

int dist[805];
bool vis[805];
int f[805][805];
vector<pair<int, int>> adj[805];
int p;
void dilkstra(int s) {
	fill(dist, dist + p + 1, 0x7fffffff);
	fill(vis, vis + p + 1, false);
	dist[s] = 0;
	priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> que;
	que.emplace(0, s);
	while (!que.empty()) {
		int u = que.top().second;
		que.pop();
		if (vis[u]) continue;
		vis[u] = true;
		for (auto &[to, w] : adj[u]) {
			if (dist[to] > dist[u] + w) {
				dist[to] = dist[u] + w;
				que.emplace(dist[to], to);
			}
		}
	}
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int n, c;
	cin >> n >> p >> c;
	vector<int> a(n);
	for (int i = 0 ; i < n ; i++) {
		cin >> a[i];
	}
	for (int i = 1 ; i <= p ; i++) {
		for (int j = 1 ; j <= p ; j++) {
			f[i][j] = 1000000;
		}
	}
	for (int i = 0 ; i < c ; i++) {
		int u, v, w;
		cin >> u >> v >> w;
		f[u][v] = min(f[u][v], w);
		f[v][u] = min(f[v][u], w);
	}
	for (int i = 1 ; i <= p ; i++) {
		for (int j = 1 ; j < i ; j++) {
			int k = f[i][j];
			if (k != 1000000) {
				adj[i].emplace_back(j, k);
				adj[j].emplace_back(i, k);
			}
		}
	}
	int ans = 0x7fffffff;
	for (int i = 1 ; i <= p ; i++) {
		dilkstra(i);
		int res = 0;
		for (int j = 0 ; j < n ; j++) {
			res += dist[a[j]];
		}
		ans = min(ans, res);
	}
	cout << ans;
	return 0;
}

C 数列排序

因为题目中数列是不重复的,所以只需要将数列排序,并重新赋值,这样只需要 即可,重新赋值后,形成了若干个环,那么只需要一直 即可求出操作次数。

时间复杂度

#include <bits/stdc++.h>
using namespace std;

// 2023 OneWan

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int n;
	cin >> n;
	vector<int> a(n), idx;
	for (int i = 0 ; i < n ; i++) {
		cin >> a[i];
		idx.push_back(a[i]);
	}
	sort(begin(idx), end(idx));
	for (int i = 0 ; i < n ; i++) {
		a[i] = lower_bound(begin(idx), end(idx), a[i]) - begin(idx);
	}
	int ans = 0;
	for (int i = 0 ; i < n ; i++) {
		while (a[i] != i) {
			int k = a[i];
			swap(a[i], a[a[i]]);
			ans++;
		}
	}
	cout << ans << "\n";
	
	return 0;
}

D 一种很新的阶乘

,由此可知时间复杂度大概在 以内。

普通的质因数分解不符合要求。

我们通过题目中给的定义可以 求出 的指数。

因为质因数分解一定会使被分解数变小,令 代表 的指数。

假设 ,那么可以得到转移方程

所以我们从 ,依次分解即可。

那么问题来了,怎么得到一个质因数,这里可以使用欧拉筛预处理。

总时间复杂度为

#include <bits/stdc++.h>
using namespace std;

// 2023 OneWan

long long cnt[10000001];
void print(int a, long long b) {
	cout << a;
	if (b > 1) {
		cout << "^" << b;
	}
}
vector<int> prime;
int vis[10000001];
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int x;
	cin >> x;
	for (int i = 2 ; i <= x ; i++) {
		if (vis[i] == 0) {
			vis[i] = i;
			prime.emplace_back(i);
		}
		for (int j = 0 ; j < int(prime.size()) && 1LL * i * prime[j] <= x ; j++) {
			vis[i * prime[j]] = prime[j];
			if (i % prime[j] == 0) {
				break;
			}
		}
	}
	for (int i = 1 ; i < x ; i++) {
		int k = x - i + 1;
		cnt[k] += i;
		if (vis[k] == k) continue;
		int z1 = vis[k], z2 = k / vis[k];
		cnt[z1] += cnt[k];
		cnt[z2] += cnt[k];
		cnt[k] = 0;
	}
	cout << "f(" << x << ")=";
	bool flag = false;
	for (int i = 1 ; i <= x ; i++) {
		if (cnt[i] != 0) {
			if (flag) {
				cout << "*";
			}
			print(i, cnt[i]);
			flag = true;
		}
	}
	return 0;
}

E 自由还是爱情 这是个问题

01背包模型,分别对 自由 和 爱情 做一次01背包。然后求符合条件的最大价值,比较再输出即可。

#include <bits/stdc++.h>
using namespace std;

// 2023 OneWan

int dp[2][2005];

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int n, k;
	cin >> n >> k;
	for (int i = 0 ; i <= 2000 ; i++) {
		dp[0][i] = dp[1][i] = 20000;
	}
	dp[0][0] = dp[1][0] = 0;
	for (int i = 1 ; i <= n ; i++) {
		int x, y, z;
		cin >> x >> y >> z;
		for (int j = 2000 ; j >= z ; j--) {
			dp[x][j] = min(dp[x][j], dp[x][j - z] + y);
		}
	}
	int mx[2] = {};
	for (int i = 2000 ; i >= 0 ; i--) {
		if (dp[0][i] <= k) {
			mx[0] = max(mx[0], i);
		}
		if (dp[1][i] <= k) {
			mx[1] = max(mx[1], i);
		}
	}
	if (mx[0] >= mx[1]) {
		cout << mx[0] << " " << 0;
	} else {
		cout << mx[1] << " " << 1;
	}
	return 0;
}

F 基本操作

模拟题,数据范围较小,根据题目暴力模拟即可,具体详细见代码。

注意:理论上来说一直复制粘贴可以使字符串很长很长,但是会使得题目不可做,本题应该是有一个最长长度限制。

#include <bits/stdc++.h>
using namespace std;

// 2023 OneWan

void solve() {
	int n, m;
	cin >> n >> m;
	string str;
	cin >> str;
	str = " " + str;
	string cx = "";
	for (int i = 0 ; i < m ; i++) {
		string s;
		cin >> s;
		if (s == "cc") {
			int L, R;
			cin >> L >> R;
			cx = str.substr(L, R - L + 1);
		} else if (s == "cv") {
			int pos;
			cin >> pos;
			if (cx != "") {
				str.insert(pos + 1, cx);
			}
		} else {
			int L, R;
			cin >> L >> R;
			cx = str.substr(L, R - L + 1);
			str.erase(begin(str) + L, begin(str) + R + 1);
		}
	}
	cout << str.substr(1) << "\n";
}

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int T;
	cin >> T;
	while (T--) {
		solve();
	}
	return 0;
}

G 夜雷の史莱姆农场

根据题意, 天后一共有 个史莱姆,但是题目说每个畜栏都有 的容量,也就是说 大于 将被清空,这转化成了 ,使用快速幂即可。

时间复杂度

#include <bits/stdc++.h>
using namespace std;

// 2023 OneWan

long long qpow(long long a, long long b, long long p) {
	long long res = 1;
	while (b) {
		if (b & 1) {
			res = res * a % p;
		}
		a = a * a % p;
		b >>= 1;
	}
	return res;
}

void solve() {
	long long a, b, p;
	cin >> a >> b >> p;
	cout << qpow(a, b, p) << "\n";
}

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int T;
	cin >> T;
	while (T--) {
		solve();
	}
	return 0;
}

H 小黑屋的救赎

的迷宫, 点体力, 钥匙。

由于数据范围极小,本题使用最短路搜索即可。

为 在 点时,使用了 把钥匙的最少体力花费。

#include <bits/stdc++.h>
using namespace std;

// 2023 OneWan

int n, m, p, k;
char g[11][11];
int sx, sy, ex, ey;
int dist[11][11][101];

const int dx[] = {0, 0, 1, -1};
const int dy[] = {1, -1, 0, 0};

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> m >> p >> k;
	for (int i = 1 ; i <= n ; i++) {
		for (int j = 1 ; j <= m ; j++) {
			cin >> g[i][j];
			if (g[i][j] == 's') {
				sx = i;
				sy = j;
			}
			if (g[i][j] == 'e') {
				ex = i;
				ey = j;
			}
			for (int z = 0 ; z <= k ; z++) {
				dist[i][j][z] = 1000;
			}
		}
	}
	queue<pair<int, int>> que;
	que.emplace(sx, sy);
	dist[sx][sy][k] = 0;
	int temp = 0;
	while (!que.empty()) {
		temp++;
		auto [x, y] = que.front();
		que.pop();
		for (int i = 0 ; i < 4 ; i++) {
			int xx = x + dx[i], yy = y + dy[i];
			if (xx < 1 || xx > n || yy < 1 || yy > m) {
				continue;
			}
			if (g[xx][yy] == 'w') continue;
			bool flag = false;
			for (int j = 0 ; j <= k ; j++) {
				if (j - (g[xx][yy] == 'd') >= 0 && dist[xx][yy][j - (g[xx][yy] == 'd')] > dist[x][y][j] + 1) {
					dist[xx][yy][j - (g[xx][yy] == 'd')] = dist[x][y][j] + 1;
					flag = true;
				}
			}
			if (flag) {
				que.emplace(xx, yy);
			}
		}
	}
	for (int i = 0 ; i <= k ; i++) {
		if (dist[ex][ey][i] <= p) {
			cout << "YES";
			return 0;
		}
	}
	cout << "NO";
	return 0;
}

I 大写数字

大模拟,本题限制整数部分最多 位,所以可以直接把整数部分扩充到 位,然后 前 位二进制枚举 ,后 位同理,小数部分最多 位,二进制枚举 即可。有其他更好做法。

#include <bits/stdc++.h>
using namespace std;

// 2023 OneWan

string num[] = {
	"零",
	"壹",
	"贰",
	"叁",
	"肆",
	"伍",
	"陆",
	"柒",
	"捌",
	"玖",
	"拾"
	};

int printF1(string str) {
	// 0000
	if (str == "0000") {
		return -1;
	}
	// 0001
	if (str[0] == '0' && str[1] == '0' && str[2] == '0') {
		cout << num[str[3] - '0'] << "万";
		return 1;
	}
	// 0010
	if (str[0] == '0' && str[1] == '0' && str[3] == '0') {
		cout << num[str[2] - '0'] << "拾万";
		return 1;
	}
	// 0100
	if (str[0] == '0' && str[2] == '0' && str[3] == '0') {
		cout << num[str[1] - '0'] << "佰万";
		return 1;
	}
	// 1000
	if (str[1] == '0' && str[2] == '0' && str[3] == '0') {
		cout << num[str[0] - '0'] << "仟万";
		return 1;
	}
	// 0011
	if (str[0] == '0' && str[1] == '0') {
		cout << num[str[2] - '0'] << "拾" << num[str[3] - '0'] << "万";
		return 1;
	}
	// 0101
	if (str[0] == '0' && str[2] == '0') {
		cout << num[str[1] - '0'] << "佰零" << num[str[3] - '0'] << "万";
		return 1;
	}
	// 1001
	if (str[1] == '0' && str[2] == '0') {
		cout << num[str[0] - '0'] << "仟零" << num[str[3] - '0'] << "万";
		return 1;
	}
	// 0110
	if (str[0] == '0' && str[3] == '0') {
		cout << num[str[1] - '0'] << "佰" << num[str[2] - '0'] << "拾万";
		return 1;
	}
	// 1010
	if (str[1] == '0' && str[3] == '0') {
		cout << num[str[0] - '0'] << "仟零" << num[str[2] - '0'] << "拾万";
		return 1;
	}
	// 1100
	if (str[2] == '0' && str[3] == '0') {
		cout << num[str[0] - '0'] << "仟" << num[str[1] - '0'] << "佰万";
		return 1;
	}
	// 0111
	if (str[0] == '0') {
		cout << num[str[1] - '0'] << "佰" << num[str[2] - '0'] << "拾" << num[str[3] - '0'] << "万";
		return 1;
	}
	// 1011
	if (str[1] == '0') {
		cout << num[str[0] - '0'] << "仟零" << num[str[2] - '0'] << "拾" << num[str[3] - '0'] << "万";
		return 1;
	}
	// 1101
	if (str[2] == '0') {
		cout << num[str[0] - '0'] << "仟" << num[str[1] - '0'] << "佰零" << num[str[3] - '0'] << "万";
		return 1;
	}
	// 1110
	if (str[3] == '0') {
		cout << num[str[0] - '0'] << "仟" << num[str[1] - '0'] << "佰" << num[str[2] - '0'] << "拾万";
		return 1;
	}
	// 1111
	cout << num[str[0] - '0'] << "仟" << num[str[1] - '0'] << "佰" << num[str[2] - '0'] << "拾" << num[str[3] - '0'] << "万";
	return 1;
}

void printF(string str) {
	while (str.size() < 8) {
		str = "0" + str;
	}
	int cnt = printF1(str.substr(0, 4));
	str = str.substr(4);
	if (str == "0000") {
		cout << "元";
		return;
	}
	// 0001
	if (str[0] == '0' && str[1] == '0' && str[2] == '0') {
		if (cnt != -1) {
			cout << "零";
		}
		cout << num[str[3] - '0'] << "元";
		return;
	}
	// 0010
	if (str[0] == '0' && str[1] == '0' && str[3] == '0') {
		if (cnt != -1) {
			cout << "零";
		}
		cout << num[str[2] - '0'] << "拾元";
		return;
	}
	// 0100
	if (str[0] == '0' && str[2] == '0' && str[3] == '0') {
		if (cnt != -1) {
			cout << "零";
		}
		cout << num[str[1] - '0'] << "佰元";
		return;
	}
	// 1000
	if (str[1] == '0' && str[2] == '0' && str[3] == '0') {
		cout << num[str[0] - '0'] << "仟元";
		return;
	}
	// 0011
	if (str[0] == '0' && str[1] == '0') {
		if (cnt != -1) {
			cout << "零";
		}
		cout << num[str[2] - '0'] << "拾" << num[str[3] - '0'] << "元";
		return;
	}
	// 0101
	if (str[0] == '0' && str[2] == '0') {
		if (cnt != -1) {
			cout << "零";
		}
		cout << num[str[1] - '0'] << "佰零" << num[str[3] - '0'] << "元";
		return;
	}
	// 1001
	if (str[1] == '0' && str[2] == '0') {
		cout << num[str[0] - '0'] << "仟零" << num[str[3] - '0'] << "元";
		return;
	}
	// 0110
	if (str[0] == '0' && str[3] == '0') {
		if (cnt != -1) {
			cout << "零";
		}
		cout << num[str[1] - '0'] << "佰" << num[str[2] - '0'] << "拾元";
		return;
	}
	// 1010
	if (str[1] == '0' && str[3] == '0') {
		cout << num[str[0] - '0'] << "仟零" << num[str[2] - '0'] << "拾元";
		return;
	}
	// 1100
	if (str[2] == '0' && str[3] == '0') {
		cout << num[str[0] - '0'] << "仟" << num[str[1] - '0'] << "佰元";
		return;
	}
	// 0111
	if (str[0] == '0') {
		if (cnt != -1) {
			cout << "零";
		}
		cout << num[str[1] - '0'] << "佰" << num[str[2] - '0'] << "拾" << num[str[3] - '0'] << "元";
		return;
	}
	// 1011
	if (str[1] == '0') {
		cout << num[str[0] - '0'] << "仟零" << num[str[2] - '0'] << "拾" << num[str[3] - '0'] << "元";
		return;
	}
	// 1101
	if (str[2] == '0') {
		cout << num[str[0] - '0'] << "仟" << num[str[1] - '0'] << "佰零" << num[str[3] - '0'] << "元";
		return;
	}
	// 1110
	if (str[3] == '0') {
		cout << num[str[0] - '0'] << "仟" << num[str[1] - '0'] << "佰" << num[str[2] - '0'] << "拾元";
		return;
	}
	// 1111
	cout << num[str[0] - '0'] << "仟" << num[str[1] - '0'] << "佰" << num[str[2] - '0'] << "拾" << num[str[3] - '0'] << "元";
}

void printB(string str) {
	while (str.size() < 2) {
		str += "0";
	}
	if (str == "00") {
		cout << "整";
		return;
	}
	if (str[0] == '0') {
		cout << "零" << num[str[1] - '0'] << "分";
		return;
	}
	if (str[1] == '0') {
		cout << num[str[0] - '0'] << "角";
		return;
	}
	cout << num[str[0] - '0'] << "角" << num[str[1] - '0'] << "分";
}

void solve() {
	string str;
	while (cin >> str) {
		string f, b = "";
		{
			int k = str.find(".");
			if (k != -1) {
				f = str.substr(0, k);
				b = str.substr(k + 1);
			} else {
				f = str;
			}
		}
		printF(f);
		printB(b);
		cout << "\n";
	}
}

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	solve();
	return 0;
}

J 兔子不会种树

易知第 秒新增 ,第 秒新增 ,...,第 秒新增 。那么

我们只需要提前处理等比数列的前 项,然后用二分或者直接循环查找即可知道 是第几秒生长出来的。

总时间复杂度

#include <bits/stdc++.h>
using namespace std;

// 2023 OneWan

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int k, n;
	cin >> k >> n;
	vector<__int128> ans;
	__int128 r = 1, sum = r;
	while (k != 1 && sum <= 1000000000000000000LL) {
		ans.emplace_back(sum);
		r *= k;
		sum += r;
	}
	for (int i = 1 ; i <= n ; i++) {
		long long x;
		cin >> x;
		if (k == 1) cout << x - 1 << "\n";
		else cout << (lower_bound(begin(ans), end(ans), x) - begin(ans)) << "\n";
	}
	return 0;
}

K 淘金币

易知 连接起来,可以得到 块钱。 连接起来,也可以得到 块钱。

那么就看 和 左边还是右边的 结合了。这里可以用dp来计算哪个更优。

为前 个字符能得到的最多钱数。

那么我们预处理所有可能的结合情况如 ,我们记录它们的左右端点。

对于 ,有

我们只需要对左端点进行排序就好了。

总时间复杂度

#include <bits/stdc++.h>
using namespace std;

// 2023 OneWan

vector<int> adj[200005];
int dp[200005];

void solve() {
	string str;
	cin >> str;
	int n = str.size();
	str = " " + str;
	for (int i = 0 ; i <= n ; i++) {
		adj[i].resize(0);
		dp[i] = 0;
	}
	int cnt = 0;
	vector<pair<int, int>> res;
	for (int i = 1 ; i <= n ; i++) {
		if (str[i] == 'A') {
			cnt++;
		} else {
			if (cnt > 0) {
				res.emplace_back(i - cnt, i);
			}
			cnt = 0;
		}
	}
	if (cnt > 0) {
		res.emplace_back(n - cnt, n);
	}
	cnt = 0;
	for (int i = n ; i >= 1 ; i--) {
		if (str[i] == 'A') {
			cnt++;
		} else {
			if (cnt > 0) {
				res.emplace_back(i, i + cnt);
			}
			cnt = 0;
		}
	}
	if (cnt > 0) {
		res.emplace_back(1, 1 + cnt);
	}
	for (auto &[L, R] : res) {
		adj[L].emplace_back(R);
	}
	for (int i = 1 ; i <= n ; i++) {
		dp[i] = max(dp[i], dp[i - 1]);
		for (auto &r : adj[i]) {
			dp[r] = max(dp[r], dp[i - 1] + r - i);
		}
	}
	cout << dp[n] << "\n";
}

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int T;
	cin >> T;
	while (T--) {
		solve();
	}
	return 0;
}

L 兔子饼干

结论题。

只有 先手才必输。

证明:

只要 阿兔 第一步选择 ,那么只会剩下 ,这是一个以 对称的形状,此时是 夜雷 的回合,那么 夜雷 操作什么,阿兔 就跟着操作什么,(nim游戏),这样子可以做到 夜雷 一定吃到 特制饼干。

时,阿兔必定吃到 特制饼干。

#include <bits/stdc++.h>
using namespace std;

// 2023 OneWan

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int n;
	while (cin >> n) {
		if (n == 1) {
			cout << "就这?就这?\n";
		} else {
			cout << "哼!哼!啊啊啊啊啊啊!\n";
		}
	}
	return 0;
}

M arkdaytime

根据题意大模拟。可以使用各种技巧来优化代码量。

#include <bits/stdc++.h>
using namespace std;

// 2023 OneWan

struct Node {
	int hp, atk, def;
	int sdef, type, pos;
	int cnt;
	friend Node operator-(Node lhs, Node rhs) {
		// lhs 守方
		// rhs 攻方
		if (rhs.type == 1) {
			lhs.hp -= rhs.atk - lhs.def;
		} else {
			lhs.hp -= (rhs.atk * (100 - lhs.sdef) + 99) / 100;
		}
		// 返回 守方
		return lhs;
	}
};
vector<Node> x;
array<vector<Node>, 2> fri;
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int n, m;
	cin >> n >> m;
	for (int i = 1 ; i <= n ; i++) {
		int a, b, c, d, e, f, g;
		cin >> a >> b >> c >> d >> e >> f >> g;
		f--;
		x.push_back({a, b, c, d, e, f, g});
	}
	reverse(begin(x), end(x));
	for (int i = 1 ; i <= m ; i++) {
		int a, b, c, d, e, f;
		cin >> a >> b >> c >> d >> e >> f;
		f--;
		fri[f].push_back({a, b, c, d, e, f, 0});
	}
	int huihe = 0;
	while (!x.empty()) {
		huihe++;
		auto di = x.back();
		x.pop_back();
		auto r = fri;
		int k = di.cnt;
		for (int i = int(r[di.pos].size()) - 1 ; k > 0 && i >= 0 ; k--, i--) {
			r[di.pos][i] = r[di.pos][i] - di;
		}
		for (int i = int(r[di.pos ^ 1].size()) - 1 ; k > 0 && i >= 0 ; k--, i--) {
			r[di.pos ^ 1][i] = r[di.pos ^ 1][i] - di;
		}
		fri[0].resize(0);
		fri[1].resize(0);
		for (int i = 0 ; i < int(r[0].size()) ; i++) {
			if (r[0][i].hp > 0) {
				fri[0].push_back(r[0][i]);
			}
		}
		if (fri[0].empty()) {
			if (di.hp > 0) {
				x.push_back(di);
			}
			cout << "No\n" << x.size();
			return 0;
		}
		for (int i = 0 ; i < int(r[1].size()) ; i++) {
			if (r[1][i].hp > 0) {
				fri[1].push_back(r[1][i]);
				di = di - fri[1].back();
			}
		}
		if (!fri[0].empty()) {
			di = di - fri[0].back();
		}
		if (di.hp > 0) {
			x.push_back(di);
		}
	}
	cout << "Yes\n" << fri[0].size() + fri[1].size();
	return 0;
}