ACM-ICPC 2018 沈阳赛区网络预赛


B. Call of Accepted

题意:计算算式,有\(d, +, -, *\)四种运算符和括号。\(d\)的级别最高,且是右结合,也就是\(xdydz = xd(ydz)\)\(xdy\)表示每次在\([1, y]\)里取一个值,取\(x\)次,求最小值和最大值。

用递归计算算式,注意细节就行。

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

using i64 = long long;

const i64 inf = 1e18;

struct Node {
	i64 min, max;
};

Node operator + (const Node & a, const Node & b) {
	return {a.min + b.min, a.max + b.max};
}

Node operator - (const Node & a, const Node & b) {
	return {a.min - b.max, a.max - b.min};
}

Node operator * (const Node & a, const Node & b) {
	i64 min = std::min({a.min * b.min, a.min * b.max, a.max * b.min, a.max * b.max});
	i64 max = std::max({a.min * b.min, a.min * b.max, a.max * b.min, a.max * b.max});
	return {min, max};
}

Node operator ^ (const Node & a, const Node & b) {
	return {a.min, a.max * b.max};
}

std::string s;
std::map<char, int> rk;

Node dfs(int l, int r) {
	if (l > r) {
		return {0, 0};
	}

	int p = -1;
	for (int i = l, cnt = 0; i <= r; ++ i) {
		if (s[i] == '(') {
			++ cnt;
		} else if (s[i] == ')') {
			-- cnt;
		} else if (cnt == 0 && !isdigit(s[i])) {
			if (p == -1 || rk[s[i]] >= rk[s[p]]) {
				p = i;
			}
		}
	}

	if (p == -1) {
		if (s[l] == '(') {
			return dfs(l + 1, r - 1);
		}

		int res = std::stoi(s.substr(l, r - l + 1));
		return {res, res};
	}

	auto L = dfs(l, p - 1);
	auto R = dfs(p + 1, r);
	if (s[p] == '+') {
		return L + R;
	} else if (s[p] == '-') {
		return L - R;
	} else if (s[p] == '*') {
		return L * R;
	} else {
		return L ^ R;
	}
}


void solve() {
	auto ans = dfs(0, (int)s.size() - 1);
	std::cout << ans.min << " " << ans.max << "\n";
}

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	rk['d'] = 1;
	rk['*'] = 2;
	rk['+'] = 3;
	rk['-'] = 3;

   	while (std::cin >> s) {
   		solve();
   	}
	return 0;
}

D. Made In Heaven

\(k\)短路板子。

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

using i64 = long long;

int n, m;

void solve() {
	int s, t, k, T;
	std::cin >> s >> t >> k >> T;
	-- s, -- t;
	std::vector<std::vector<std::pair<int, i64>>> adj(n);
	std::vector<std::vector<std::pair<int, i64>>> radj(n);
	for (int i = 0; i < m; ++ i) {
        int u, v;
        i64 w;
		std::cin >> u >> v >> w;
		-- u, -- v;
		adj[u].emplace_back(v, w);
		radj[v].emplace_back(u, w);
	}

	const i64 inf = 1e18;
	auto dijkstra = [&](int s) -> std::vector<i64> {
		std::vector<i64> dist(n, inf);
		using A = std::pair<i64, int>;
		std::priority_queue<A, std::vector<A>, std::greater<A>> heap;
		dist[s] = 0;
		heap.emplace(0, s);
		while (heap.size()) {
			auto [d, u] = heap.top(); heap.pop();
			if (d != dist[u]) {
				continue;
			}

			for (auto & [v, w] : radj[u]) {
				if (dist[v] > dist[u] + w) {
					dist[v] = dist[u] + w;
					heap.emplace(dist[v], v);
				}
			}
		}

		return dist;
	};

	auto f = dijkstra(t);
	auto a_star = [&](int s, int t) -> i64 {
		if (f[s] > T) {
			return inf;
		}
		using A = std::tuple<i64, i64, int>;
		std::priority_queue<A, std::vector<A>, std::greater<A>> heap;
		heap.emplace(f[s], 0, s);
		int cur = 0;
		while (heap.size()) {
			auto [_, d, u] = heap.top(); heap.pop();
			if (u == t) {
				++ cur;
			}

			if (cur == k) {
				return d;
			}

			if (d > T) {
				return inf;
			}

			for (auto & [v, w] : adj[u]) {
				heap.emplace(d + w + f[v], d + w, v);
			}
		}

		return inf;
	};

	if (a_star(s, t) <= T) {
		std::cout << "yareyaredawa\n";
	} else {
		std::cout << "Whitesnake!\n";
	}
}

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

F. Fantastic Graph

题意:给你一个二分图和一些边,你需要选择一些边,使得每个点的度数都在\([L, R]\)里。

把原图边视为上界为\(1\)下界为\(0\)的边,然后建立源点和汇点,源点向左部点连上界为\(R\)下界为\(L\)的边,右部点向汇点连上界为\(R\)下界为\(L\)的边。这样就变成了有源汇的上下界可行流问题。

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

using i64 = long long;

const int N = 4010, M = (6000 + 4000 * 2 + 5) * 2 + 10, inf = 1e8;

int head[N], ver[M], next[M], low[M], cap[M], tot;
int d[N], cur[N], A[N];
int n, m, k, S, T;

void add(int x, int y, int l, int u) {
	A[x] -= l;
	A[y] += l;
	ver[tot] = y; cap[tot] = u - l; low[tot] = l; next[tot] = head[x]; head[x] = tot ++ ;
	ver[tot] = x; cap[tot] = 0; next[tot] = head[y]; head[y] = tot ++ ;
}

bool bfs() {
	std::queue<int> q;
	memset(d, -1, sizeof d);
	q.push(S);
	d[S] = 0; cur[S] = head[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 L, R;
	std::cin >> L >> R;
	memset(head, -1, sizeof head);
	tot = 0;
	memset(A, 0, sizeof A);
	S = n + m + 3, T = n + m + 4;
	int s = n + m + 1, t = n + m + 2;
	for (int i = 0; i < k; ++ i) {
		int u, v;
		std::cin >> u >> v;
		add(u, v + n, 0, 1);
	}

	for (int i = 1; i <= n; ++ i) {
		add(s, i, L, R);
	}

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

	add(t, s, 0, inf);

	int sum = 0;
	for (int i = 1; i <= n + m + 2; ++ i) {
		if (A[i] > 0) {
			sum += A[i];
			add(S, i, 0, A[i]);
		} else if (A[i] < 0) {
			add(i, T, 0, -A[i]);
		}
	}

	if (dinic() != sum) {
		std::cout << "No\n";
	} else {
		std::cout << "Yes\n";
	}
}

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
    while (std::cin >> n >> m >> k) {
    	if (n == 0) {
    		break;
    	}

 		std::cout << "Case " << t ++ << ": ";
 		solve();   	
    }
	return 0;
}

G. Spare Tire

题意:给你\(n, m\),对于\([1, n]\)里每个和\(m\)互质的\(i\),求\(a_i\)的和。

首先打表发现\(a_i = i^2 + i\)
那么我们可以用容斥的思想,先把所有数的和算上:\(\sum_{i=1}^{n} i^2+i\)\(i^2\)\(i\)的和都可以直接算。
然后我们给\(m\)分解质因子,枚举质因子的所有组合,假设现在乘积为\(x\),那么这个组合包含的所有数就是\(\sum_{k=1}^{\lfloor \frac{n}{x} \rfloor} (kx)^2 + kx\)。根据质因子个数的奇偶判断是加上还是减去就行。这个式子也容易计算。

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

using i64 = long long;

const int mod = 1e9 + 7;

int n, m;

std::vector<int> primes, minp;

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

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

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

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

	return res;
}

const int inv2 = power(2, mod - 2), inv6 = power(6, mod - 2);

int get1(int n) {
	return (i64)n * (n + 1) % mod * inv2 % mod;
}

int get2(int n) {
	return (i64)n * (n + 1) % mod * (2 * n % mod + 1) % mod * inv6 % mod;
}

void solve() {
	std::vector<int> a;
	for (auto & p : primes) {
		if (p * p > m) {
			break;
		}

		if (m % p == 0) {
			a.push_back(p);
			while (m % p == 0) {
				m /= p;
			}
		}
	}

	if (m > 1) {
		a.push_back(m);
	}

	int ans = (get1(n) + get2(n)) % mod;
	int tot = a.size();
	for (int i = 1; i < 1 << tot; ++ i) {
		int cnt = __builtin_popcount(i);
		int cur = 1;
		for (int j = 0; j < tot; ++ j) {
			if (i >> j & 1) {
				cur *= a[j];
			}
		}

		if (cur > n) {
			continue;
		}

		int k = n / cur;
		int val = ((i64)get1(k) * cur % mod + (i64)get2(k) * cur % mod * cur % mod) % mod;
		if (cnt & 1) {
			ans = (ans - val + mod) % mod;
		} else {
			ans = (ans + val) % mod;
		}
	}

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

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	init(10000);
   	while (std::cin >> n >> m) {
   		if (n == 0) {
   			break;
   		}
   		solve();
   	}
	return 0;
}

I. Lattice&s basics in digital electronics

模拟

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

using i64 = long long;

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

	auto get = [&](char c) -> std::string {
		int n = c - '0';
		if (c >= 'A' && c <= 'Z') {
			n = 10 + c - 'A';
		}

		std::string res;
		for (int i = 3; i >= 0; -- i) {
			res += (n >> i & 1) ? '1' : '0';
		}

		return res;
	};


	std::string s;
	std::cin >> s;
	std::string t;
	for (auto & c : s) {
		if (c >= 'a' && c <= 'z') {
			c -= 32;
		}

		t += get(c);
	}

	s.clear();
	for (int i = 0; i + 8 < t.size(); i += 9) {
		int cnt = 0;
		for (int j = i; j < i + 9; ++ j) {
			cnt += t[j] == '1';
		}

		if (cnt & 1) {
			s += t.substr(i, 8);
		}
	}

	std::string ans;
	t.clear();
	for (auto & c : s) {
		t += c;
		if (mp.count(t)) {
			ans += (char)mp[t];
			t.clear();
		}

		if (ans.size() > n) {
			break;
		}
	}

	std::cout << ans.substr(0, n) << "\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. Ka Chang

题意:给你一棵以一为根的树,两种操作,一种是给深度为\(l\)的点都加上\(x\),一种是查询\(u\)的子数和。

根号分治。
对于第一个操作,如果深度为\(l\)的点小于等于\(\sqrt{n}\)个,直接暴力加就行,可以用树状数组维护\(dfs\)序。否则用个数组存一下深度为\(l\)的点都加了多少。
对于第二个操作,先计算\(dfs\)序子树区间的和。然后我们可以预处理用\(map\)存每个点有多少个深度大于\(\sqrt{n}\)的点,因为这样的深度个数不超过\(\frac{n}{\sqrt{n}}\)个,不会爆炸。然后枚举所有这些深度,看有多少点在这个深度累加答案就行了。

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

using i64 = long long;

struct Fenwick {
	std::vector<i64> tr;
	int n;
	Fenwick(){};
	Fenwick(int _n) {
		n = _n;
		tr.assign(_n + 1, 0);
	}

	void add(int x, int v) {
		for (int i = x; i <= n; i += i & -i) {
			tr[i] += v;
		}
	}

	i64 query(int x) {
		i64 res = 0;
		for (int i = x; i > 0; i -= i & -i) {
			res += tr[i];
		}

		return res;
	}

	i64 sum(int l, int r) {
		return query(r) - query(l - 1);
	}
};

void solve() {
	int n, q;
	std::cin >> n >> q;
	std::vector<std::vector<int>> adj(n);
	for (int i = 1; i < n; ++ i) {
		int u, v;
		std::cin >> u >> v;
		-- u, -- v;
		adj[u].push_back(v);
	}

	std::vector<int> in(n), out(n), d(n);
	std::vector<std::vector<int>> a(n);
	int idx = 0;
	auto dfs = [&](auto & self, int u) -> void {
		in[u] = idx ++ ;
		a[d[u]].push_back(u);
		for (auto & v : adj[u]) {
			d[v] = d[u] + 1;
			self(self, v);
		}
		out[u] = idx - 1;
	};

	dfs(dfs, 0);

	int T = std::sqrt(n);
	std::vector<std::map<int, int>> mp(n);
	auto dfs1 = [&](auto & self, int u) -> void {
		for (auto & v : adj[u]) {
			self(self, v);
			for (auto & [x, y] : mp[v]) {
				mp[u][x] += y;
			}
		}

		if (a[d[u]].size() >= T) {
			mp[u][d[u]] += 1;
		}
	};

	dfs1(dfs1, 0);

	std::vector<i64> sum(n);
	Fenwick tr(2 * n);
	while (q -- ) {
		int op;
		std::cin >> op;
		if (op == 1) {
			int l, x;
			std::cin >> l >> x;
			if (a[l].size() <= T) {
				for (auto & u : a[l]) {
					tr.add(in[u] + 1, x);
				}
			} else {
				sum[l] += x;
			}
		} else {
			int u;
			std::cin >> u;
			-- u;
			i64 ans = tr.sum(in[u] + 1, out[u] + 1);
			for (auto & [x, y] : mp[u]) {
				ans += sum[x] * y;
			}
			std::cout << ans << "\n";
		}
	}
}

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

K. Supreme Number

题意:一个数的所以子序列都是质数这个数就是好的,求小于等于\(n\)的最大的好数。

显然\(1\)最多出现两次,因为\(111\)不是质数,其它数最多出现一次,因为\(xx\)\(11\)的倍数。
那么合法的数在\(1000\)以内,暴力枚举出来就行。

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

using i64 = long long;

std::vector<int> a;

bool is_primes(int n) {
	for (int i = 2; i * i <= n; ++ i) {
		if (n % i == 0) {
			return false;
		}
	}

	return true;
}

bool check(int n) {
	std::string s = std::to_string(n);
	auto dfs = [&](auto & self, int u, int x) -> bool {
		if (u == s.size()) {
			return is_primes(x);
		}

		if (!self(self, u + 1, x) || !self(self, u + 1, x * 10 + s[u] - '0')) {
			return false;
		}

		return true;
	};

	return dfs(dfs, 0, 0);
}

void init() {
	for (int i = 1; i <= 999; ++ i) {
		if (check(i)) {
			a.push_back(i);
		}
	}
}

void solve() {
	std::string s;
	std::cin >> s;
	if (s.size() >= 4) {
		std::cout << a.back() << "\n";
	} else {
		auto it = std::upper_bound(a.begin(), a.end(), std::stoi(s));
		-- it;
		std::cout << *it << "\n";
	}
}

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	init();
	std::cin >> t;
	for (int i = 1; i <= t; ++ i) {
		std::cout << "Case #" << i << ": ";
		solve();
	}
	return 0;
}
posted @ 2025-05-22 19:58  maburb  阅读(21)  评论(0)    收藏  举报