VP 2018-2019 ACM-ICPC, Asia Nanjing Regional Contest


A. Adrien and Austin

题意:\(n\)个石头,每次可以连续拿\(1\)\(k\)个,拿过的不可以再拿。不可以拿的输。求赢家。

大量试样例后,觉得除了\(k=1\)的情况需要讨论奇偶,其它情况都是第一个人赢。注意\(n=0\)是第二个人赢。

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

void solve() {
	int n, k;
	std::cin >> n >> k;
	if (n == 0 || (k == 1 && n % 2 == 0)) {
		std::cout << "Austin\n";
	} else {
		std::cout << "Adrien\n";
	}
}

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	//std::cin >> t;
	while (t -- ) {
		solve();
	}
	return 0;
}

D. Country Meow

题意:给你\(n\)个三维点,你要选一个点,使得它和最远的点的距离最小。

如果是二维,在固定了\(x\)的情况下,其实是个开口向下的二次函数,那么猜测三维在固定\(x, y\)的情况下也是单峰函数。套三个三分就行了。

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

void solve() {
	int n;
	std::cin >> n;
	std::vector<int> x(n), y(n), z(n);
	for (int i = 0; i < n; ++ i) {
		std::cin >> x[i] >> y[i] >> z[i];
	}

	auto get = [&](double x1, double y1, double z1, double x2, double y2, double z2) -> double {
		double dx = x1 - x2, dy = y1 - y2, dz = z1 - z2;
		return dx * dx + dy * dy + dz * dz;
	};

	auto check2 = [&](double X, double Y, double Z) -> double {
		double res = 0;
		for (int i = 0; i < n; ++ i) {
			res = std::max(res, get(X, Y, Z, x[i], y[i], z[i]));
		}
		return res;
	};

	auto check1 = [&](double x, double y) -> double {
		double l = -100000, r = 100000;	
		double ans = 1e18;
		for (int i = 0; i < 100; ++ i) {
			double mid1 = l + (r - l) / 3, mid2 = l + (r - l) * 2 / 3;
			double ansl = check2(x, y, mid1), ansr = check2(x, y, mid2);
			ans = std::min({ans, ansl, ansr});
			if (ansl < ansr) {
				r = mid2;
			} else {
				l = mid1;
			}
		}

		return ans;
	};

	auto check = [&](double x) -> double {
		double l = -100000, r = 100000;	
		double ans = 1e18;
		for (int i = 0; i < 100; ++ i) {
			double mid1 = l + (r - l) / 3, mid2 = l + (r - l) * 2 / 3;
			double ansl = check1(x, mid1), ansr = check1(x, mid2);
			ans = std::min({ans, ansl, ansr});
			if (ansl < ansr) {
				r = mid2;
			} else {
				l = mid1;
			}
		}

		return ans;
	};

	double l = -100000, r = 100000;
	double ans = 1e18;
	for (int i = 0; i < 100; ++ i) {
		double mid1 = l + (r - l) / 3, mid2 = l + (r - l) * 2 / 3;
		double ansl = check(mid1), ansr = check(mid2);
		ans = std::min({ans, ansl, ansr});
		if (ansl < ansr) {
			r = mid2;
		} else {
			l = mid1;
		}
	}

	std::cout << std::fixed << std::setprecision(12);
	std::cout << std::sqrt(ans) << "\n";
}

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	//std::cin >> t;
	while (t -- ) {
		solve();
	}
	return 0;
}

G. Pyramid

题意:\(n\)层的三角形金字塔有多少等边三角形,包含斜着的。

不好说,只能打表模拟。得出\(C(n + 3, 4)\)

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

const int mod = 1e9 + 7;

i64 power(i64 a, i64 b) {
	i64 res = 1;
	for (;b;b >>= 1, a = a * a % mod) {
		if (b & 1) {
			res = res * a % mod;
		}
	}

	return res;
}

i64 inv = power(24, mod - 2);

void solve() {
	i64 n;
	std::cin >> n;
	i64 ans = (n + 3) * (n + 2) % mod * (n + 1) % mod * n % mod * inv % mod;
	std::cout << ans << "\n";
}

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	std::cin >> t;
	while (t -- ) {
		solve();
	}
	return 0;
}

I. Magic Potion

题意:\(n\)个英雄\(m\)个怪物。每个英雄只能杀固定的一些怪物,且只能杀一个。但你可以选择\(k\)个不同的英雄让他们多杀一个。求最多杀多少怪物。

虽说是很简单的网络流,但签到题考网络流合理吗。
其实是个二分图匹配,但有些点可以多匹配一个,新建一个点从源点给它连容量为\(k\)的边,然后这个点向每个英雄连容量为\(1\)的边。

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

const int N = 1000 + 10, M = (500 * 500 + 500 * 3 + 1) * 2 + 10, INF = 1e8;

int head[N], ver[M], cap[M], next[M], tot;
int d[N], cur[N];
int S, T;

void add(int x, int y, int z) {
	ver[tot] = y; cap[tot] = z; next[tot] = head[x]; head[x] = tot ++ ;
	ver[tot] = x; cap[tot] = 0; next[tot] = head[y]; head[y] = tot ++ ;
}

bool bfs() {
	memset(d, -1, sizeof d);
	std::queue<int> q;
	d[S] = 0, cur[S] = head[S];
	q.push(S);
	while (q.size()) {
		int u = q.front(); q.pop();
		for (int i = head[u]; i != -1; i = next[i]) {
			int v = ver[i];
			if (d[v] == -1 && cap[i]) {
				d[v] = d[u] + 1;
				cur[v] = head[v];
				if (v == T) {
					return true;
				}

				q.push(v);
			}
		}
	}

	return false;
}

int find(int u, int limit) {
	if (u == T) {
		return limit;
	}

	int flow = 0;
	for (int i = cur[u]; i != -1 && flow < limit; i = next[i]) {
		int v = ver[i];
		cur[u] = i;
		if (d[v] == d[u] + 1 && cap[i]) {
			int t = find(v, std::min(cap[i], limit - flow));
			if (t == 0) {
				d[v] = -1;
			} else {
				cap[i] -= t; cap[i ^ 1] += t;
				flow += t;
			}
		}
	}

	return flow;
}

int dinic() {
	int r = 0, flow;
	while (bfs()) {
		while (flow = find(S, INF)) {
			r += flow;
		}
	}

	return r;
}

void solve() {
	int n, m, k;
	std::cin >> n >> m >> k;
	S = n + m + 1, T = n + m + 2;
	int V = n + m + 3;
	memset(head, -1, sizeof head);
	for (int i = 1; i <= n; ++ i) {
		int t;
		std::cin >> t;
		while (t -- ) {
			int j;
			std::cin >> j;
			add(i, j + n, 1);
		}
	}

	add(S, V, k);
	for (int i = 1; i <= n; ++ i) {
		add(S, i, 1);
		add(V, i, 1);
	}

	for (int i = 1; i <= m; ++ i) {
		add(i + n, T, 1);
	}

	std::cout << dinic() << "\n";
}

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	//std::cin >> t;
	while (t -- ) {
		solve();
	}
	return 0;
}

J. Prime Game

题意:给你一个数组,求所有子区间乘积的不同质因子的个数的和。

对于每个数枚举它的质因子,记录每个质因子上一次出现的位置为\(last\),那么这个质因子贡献为\((i - last) \times (n - i + 1)\)

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

std::vector<int> primes, minp;

void sieve(int n) {
	primes.clear();
	minp.assign(n + 1, 0);
	for (int i = 2; i <= n; ++ i) {
		if (minp[i] == 0) {
			minp[i] = i;
			primes.push_back(i);
		}

		for (auto & p : primes) {
			if (p * i > n) {
				break;
			}

			minp[p * i] = p;
			if (minp[i] == p) {
				break;
			}
		}
	}
}

void solve() {
	sieve(1e6);
	int n;
	std::cin >> n;
	std::vector<int> a(n + 1);
	for (int i = 1; i <= n; ++ i) {
		std::cin >> a[i];
	}

	std::map<int, int> last;
	i64 ans = 0;
	for (int i = 1; i <= n; ++ i) {
		while (a[i] > 1) {
			int t = minp[a[i]];
			ans += (i64)(i - last[t]) * (n - i + 1);
			while (a[i] % t == 0) {
				a[i] /= t;
			}
			last[t] = i;
		}
	}

	std::cout << ans << "\n";
}

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	//std::cin >> t;
	while (t -- ) {
		solve();
	}
	return 0;
}

K. Kangaroo Puzzle

题意:一个矩阵,有些地方不能走,其它地方都有一个袋鼠,你每次下达上下左右的命令,所有袋鼠如果能走就会往这个方向走,你要让所有袋鼠在同一个位置。

找一个空地,然后让其它所有位置都找一条路径到这个位置。感觉没问题,结果\(wa\)了,然后把答案复制一遍到后面输出就过了。

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

void solve() {
	int n, m;
	std::cin >> n >> m;
	std::vector<std::string> s(n);
	for (int i = 0; i < n; ++ i) {
		std::cin >> s[i];
	}

	int sx = 0, sy = 0;
	for (int i = 0; i < n; ++ i) {
		bool flag = false;
		for (int j = 0; j < m; ++ j) {
			if (s[i][j] == '1') {
				sx = i, sy = j;
				flag = true;
				break;
			}
		}

		if (flag) {
			break;
		}
	}

	const int dx[] = {-1, 1, 0, 0}, dy[] = {0, 0, -1, 1};
	std::vector d(n, std::vector<int>(m, -1));
	std::vector pre(n, std::vector<int>(m, -1));
	std::queue<std::pair<int, int>> q;
	d[sx][sy] = 0;
	q.emplace(sx, sy);
	while (q.size()) {
		auto [x, y] = q.front(); q.pop();
		for (int i = 0; i < 4; ++ i) {
			int nx = x + dx[i], ny = y + dy[i];
			if (nx < 0 || nx >= n || ny < 0 || ny >= m || d[nx][ny] != -1 || s[nx][ny] == '0') {
				continue;
			}

			d[nx][ny] = d[x][y] + 1;
			pre[nx][ny] = i ^ 1;
			q.emplace(nx, ny);
		}
	}
	std::string op = "UDLR";
	std::string ans;
	for (int i = 0; i < n; ++ i) {
		for (int j = 0; j < m; ++ j) {
			if (s[i][j] == '0') {
				continue;
			}
			int x = i, y = j;
			while (pre[x][y] != -1) {
				int t = pre[x][y];
				ans += op[t];
				x += dx[t], y += dy[t];
			}
		}
	}

	ans += ans;

	std::cout << ans << "\n";
}

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	//std::cin >> t;
	while (t -- ) {
		solve();
	}
	return 0;
}
posted @ 2025-05-21 00:46  maburb  阅读(37)  评论(0)    收藏  举报