比赛链接:

https://codeforces.com/gym/104023

A. Dunai

题意:

\(n\) 个队伍获得过冠军,告知每个队伍中的人及对应的位置,现在已知 \(m\) 个选手及它们的位置,问能组成多少个五个人,满足每个位置上有一人且队伍中至少一人拿过冠军。

思路:

答案即每个位置人数的最小值与冠军总数中小的那个值。

代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	int n;
	cin >> n;
	map<string, int> Map;
	for (int i = 1; i <= n; i ++ ){
		for (int j = 1; j <= 5; j ++ ){
			string s;
			cin >> s;
			Map[s] = 1;
		}
	}
	int m;
	cin >> m;
	vector<string> t[6];
	for (int i = 1; i <= m; i ++ ){
		string s;
		int id;
		cin >> s >> id;
		t[id].push_back(s);
	}
	vector<int> cnt(6);
	for (int i = 1; i <= 5; i ++ ){
		for (auto x : t[i]){
			if (Map[x]){
				cnt[i] ++ ;
			}
		}
	}
	int ans = 2000;
	for (int i = 1; i <= 5; i ++ ){
		ans = min(ans, (int)t[i].size());
	}
	cout << min(ans, accumulate(cnt.begin(), cnt.end(), 0)) << "\n";
	return 0;
}

C. Grass

题意:

给定平面上 \(n\) 个点,选择四个点 \(B,C,D,E\) 以及一个中心点 \(A\),要求四个点与中心点的连线不相交,问是否存在这样的五个点。

思路:

可以发现只要五点不共线就一定满足要求,固定四个点,枚举第五个点,判断是否五点共线(通过斜率可以判断)。
接着选取一个点作为中心点,然后判断它是不是可以作为中心(先选择一个点,判断剩余三个是不是在它与中心的连线上)。

代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
void solve(){
	int n;
	cin >> n;
	vector<LL> x(n), y(n);
	for (int i = 0; i < n; i ++ )
		cin >> x[i] >> y[i];
	if (n < 5){
		cout << "NO\n";
		return;
	}
	vector<int> p = {0, 1, 2, 3, 4};
	auto check = [&](){
		for (int i = 1; i < 4; i ++ ){
			if ((y[p[i]] - y[p[i - 1]]) * (x[p[i + 1]] - x[p[i]]) != (y[p[i + 1]] - y[p[i]]) * (x[p[i]] - x[p[i - 1]])){
				return true;
			}
		}
		return false;
	};
	auto judge = [&](int c){
		for (int a = 0; a < 5; a ++ ){
			if (a == c) continue;
			for (int b = 0; b < 5; b ++ ){
				if (b == a || b == c) continue;
				int minx = min(x[p[a]], x[p[c]]);
				int maxx = max(x[p[a]], x[p[c]]);
				int miny = min(y[p[a]], y[p[c]]);
				int maxy = max(y[p[a]], y[p[c]]);
				if (minx <= x[p[b]] && x[p[b]] <= maxx && miny <= y[p[b]] && y[p[b]] <= maxy && ((y[p[b]] - y[p[a]]) * (x[p[c]] - x[p[b]]) == (y[p[c]] - y[p[b]]) * (x[p[b]] - x[p[a]]))){
					return false;
				}
			}
		}
		return true;
	};
	for (int i = 4; i < n; i ++ ){
		p[4] = i;
		if (check()){
			cout << "YES\n";
			for (int c = 0; c < 5; c ++ ){
				if (judge(c)){
					cout << x[p[c]] << " " << y[p[c]] << "\n";
					for (int j = 0; j < 5; j ++ ){
						if (c == j) continue;
						cout << x[p[j]] << " " << y[p[j]] << "\n";
					}
					return;
				}
			}
		}
	}
	cout << "NO\n";
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	int T;
	cin >> T;
	while(T -- ){
		solve();
	}
	return 0;
}

D. Sternhalma

题意:

19 个格子的六边形跳棋棋盘,上面有若干棋子,有两种操作:
1.移除一个棋子,获得 0 分。
2.某个棋子跳过相邻的一个棋子,移除被跳的那个棋子,获得该位置的得分。
现在已知每个位置的分数,告诉若干个刚开始棋子的布局,求出每个移除所有棋子后的最大得分。

思路:

总共 19 个格子,通过状压将每个状态都表示出来。从没有棋子的状态(即 0,这是逆向的,所有操作反一下)出发进行 \(dfs\)
有两种转移的状态:
1.直接加上一个棋子,不得分。
2.跳棋,有六种情况,左上到右下,右上到左下,左到右,或者方向反一下。
依次 \(dfs\) 即可。

代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	vector<int> w(19);
	for (int i = 0; i < 19; i ++ )
		cin >> w[i];
	vector<int> L = {-1, 0, 1, -1, 3, 4, 5, -1, 7, 8, 9, 10, -1, 12, 13, 14, -1, 16, 17};
	vector<int> R = {1, 2, -1, 4, 5, 6, -1, 8, 9, 10, 11, -1, 13, 14, 15, -1, 17, 18, -1};
	vector<int> UL = {-1, -1, -1, -1, 0, 1, 2, -1, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13, 14};
	vector<int> UR = {-1, -1, -1, 0, 1, 2, -1, 3, 4, 5, 6, -1, 8, 9, 10, 11, 13, 14, 15};
	vector<int> DL = {3, 4, 5, 7, 8, 9, 10, -1, 12, 13, 14, 15, -1, 16, 17, 18, -1, -1, -1};
	vector<int> DR = {4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, -1, 16, 17, 18, -1, -1, -1, -1};
	vector<int> dp((1 << 19) - 1, -1e9);
	dp[0] = 0;
	function<void(int)> dfs = [&](int st){
		auto add = [&](int st, int i, int j, int k){
			if (!(st & (1 << i)) && (st & (1 << k))){
				if (dp[st] + w[j] > dp[st ^ (1 << i) ^ (1 << j) ^ (1 << k)]){
					dp[st ^ (1 << i) ^ (1 << j) ^ (1 << k)] = dp[st] + w[j];
					dfs(st ^ (1 << i) ^ (1 << j) ^ (1 << k));
				}
			}
		};
		for (int i = 0; i < 19; i ++ ){
			if (!(st & (1 << i))){
				if (UL[i] != -1 && DR[i] != -1) add(st, UL[i], i, DR[i]);
				if (DR[i] != -1 && UL[i] != -1) add(st, DR[i], i, UL[i]);
				if (UR[i] != -1 && DL[i] != -1) add(st, UR[i], i, DL[i]);
				if (DL[i] != -1 && UR[i] != -1) add(st, DL[i], i, UR[i]);
				if (L[i] != -1 && R[i] != -1) add(st, L[i], i, R[i]);
				if (R[i] != -1 && L[i] != -1) add(st, R[i], i, L[i]);
				if (dp[st] > dp[st ^ (1 << i)]){
					dp[st ^ (1 << i)] = dp[st];
					dfs(st ^ (1 << i));
				}
			}
		}
	};
	dfs(0);
	int n;
	cin >> n;
	while(n -- ){
		int st = 0;
		for (int i = 0; i < 19; i ++ ){
			char c;
			cin >> c;
			if (c == '#') st ^= (1 << i);
		}
		cout << dp[st] << "\n";
	}
	return 0;
}

E. Python Will be Faster than C++

题意:

已知 \(n\) 个版本的运行速度,现在要找到第一个比 \(k\) 小的版本。已知后续第 \(i\) 个版本的运行速度为 \(max(2 * a_{i - 1} - a_{i - 2}, 0)\)

思路:

只有当最后两个版本是递减的时候,才会有答案。可以发现后面的就是一个等差,根据公式求一下即可。

代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	int n, k;
	cin >> n >> k;
	vector<int> a(n + 1);
	for (int i = 1; i <= n; i ++ )
		cin >> a[i];
	int d = a[n] - a[n - 1];
	if (d >= 0){
		cout << "Python will never be faster than C++\n";
	}
	else{
		int t = (a[n] - k) / d;
		cout << "Python 3." << abs(t) + n + 1 << " will be faster than C++\n";
	}
	return 0;
}

G. Grade 2

题意:

给定 \(x\)\(n\)\(L, R\)。求 \(\sum_{k = L}^R [gcd(kx \bigoplus x, x) = 1]\)

思路:

打表可以发现循环节为 \(2^{\lceil log_2x \rceil}\)。暴力跑出前 \(2^{\lceil log_2x \rceil}\) 个,然后直接求解。

代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	LL x, n;
	cin >> x >> n;
	LL T = 1;
	while(T < x){
		T <<= 1;
	}
	vector<LL> sum(T + 1);
	for (int k = 1; k <= T; k ++ ){
		sum[k] = sum[k - 1] + (gcd((k * x) ^ x, x) == 1);
	}
	auto calc = [&](LL x){
		return (x - 1) / T * sum[T] + sum[(x - 1) % T + 1];
	};
	for (int i = 1; i <= n; i ++ ){
		LL L, R;
		cin >> L >> R;
		cout << calc(R) - calc(L - 1) << "\n";
	}
	return 0;
}

I. Dragon Bloodline

题意:

一个龙蛋需要 \(n\) 种原料,第 \(i\) 种原料需要 \(a_i\) 个,现在有 \(k\) 种龙,第 \(i\) 种龙有 \(b_i\) 个,当它被指定去生产某种原料后,它可以采集这种原料 \(2^i\) 个,问最多能生产多少龙蛋。

思路:

随着龙蛋数量的增多,所需的原料也增多,考虑二分答案。
按照龙的生产效率从大到小,所需的原料通过优先队列进行排列,当某种原料的需要大于等于龙的生产力的时候,该条龙被分配去生产这种原料显然是最贪的,如果该种原料还需要龙生产,再将它加入队列中。当生产力大于取出来的原料时(堆顶的元素是最大的),直接删除这个值即可,因为没办法避免生产力的浪费了。
注意二分的上限,如果不加限制会爆,可用 int128 或限制上限。

代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
void solve(){
	int n, k;
	cin >> n >> k;
	vector<LL> a(n);
	for (int i = 0; i < n; i ++ )
		cin >> a[i];
	vector<LL> b(k);
	for (int i = 0; i < k; i ++ )
		cin >> b[i];
	auto check = [&](LL x){
		auto c = b;
		priority_queue<LL> q;
		for (int i = 0; i < n; i ++ )
			q.push(a[i] * x);
		for (int j = k - 1; j >= 0; j -- ){
			while(!q.empty() && c[j]){
				LL x = q.top();
				q.pop();
				LL p = min(c[j], x / (1 << j));
				if (p == 0){
					c[j] -- ;
				}
				else{
					c[j] -= p;
					x -= p * (1 << j);
					if (x){
						q.push(x);
					}
				}
			}
		}
		return q.empty();
	};
	LL L = 0, R = 0;
	for (int i = 0; i < k; i ++ ){
		R += (1 << i) * b[i];
	}
	R /= *min_element(a.begin(), a.end());
	while(L < R){
		LL mid = L + R + 1 >> 1;
		if (check(mid)){
			L = mid;
		}
		else{
			R = mid - 1;
		}
	}
	cout << L << "\n";
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	int T;
	cin >> T;
	while(T -- ){
		solve();
	}
	return 0;
}

J. Eat, Sleep, Repeat

题意:

给定 \(n\) 个数,\(k\) 个限制,限制 \(i\) 限制数 \(x_i\) 不能超过 \(y_i\) 个。
\(Pico\)\(FuuFuu\) 轮流选择一个数将它的值减一,前者先手,二者均采用最优策略。当某条限制不满足或者所有数都为 0 时,当前这个人输,问先手胜还是后手胜。

思路:

全部为 0 的可以看成 -1,0 这个限制。
依据所有 \(x\) 不能超过 0 个的限制,将所有数划分为若干个区间。因为这些数不可能会低于 \(x\),同时它们要满足其它限制,所以它们最后的值可以确定。求出这些值的总和,判断奇偶即可。

代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
void solve(){
	int n, k;
	cin >> n >> k;
	vector<int> a(n);
	for (int i = 0; i < n; i ++ )
		cin >> a[i];
	vector<pair<int, int>> lim(k + 1);
	map<int, int> Map;
	for (int i = 1; i <= k; i ++ ){
		cin >> lim[i].first >> lim[i].second;
		Map[lim[i].first] = lim[i].second;
	}
	sort(a.begin(), a.end());
	lim[0] = {-1, 0};
	sort(lim.begin(), lim.end());
	LL ans = 0;
	for (int j = k, i = n - 1; j >= 0; j -- ){
		if (lim[j].second) continue;
		int ti = i, t = lim[j].first + 1;
		while(ti >= 0 && a[ti] >= t){
			ti -- ;
		}
		for (int it = ti + 1; it <= i; it ++ ){
			while(Map.count(t) && Map[t] == 0){
				t ++ ;
			}
			ans += a[it] - t;
			if (Map.count(t)){
				Map[t] -- ;
			}
		}
		i = ti;
	}
	cout << ((ans & 1) ? "Pico" : "FuuFuu") << "\n";
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	int T;
	cin >> T;
	while(T -- ){
		solve();
	}
	return 0;
}
posted on 2022-11-14 13:51  Hamine  阅读(358)  评论(0编辑  收藏  举报