Codeforces Round 888 (Div. 3) - D(思维) E(toposort) F(位运算贪心)

Codeforces Round 888 (Div. 3) 赛后摘记

ABCD简单题


A : \(O(n)\)判断每一个人与 Vlad 的高度差是不是楼梯高 k 的 c 整数倍 \((1\le c < m)\)

void solve(){
	ll n, m, k, H;
	cin >> n >> m >> k >> H;
	vector<ll> h(n + 1);
	ll ans = 0;
	for(int i = 1; i <= n; ++ i){
		cin >> h[i];
		if(abs(h[i] - H) % k == 0 && abs(h[i] - H) / k < m && h[i] != H){
			++ ans;
			// debug(i);
		}
	}
	cout << ans << '\n';
	return ;
}

B :先对整个数组进行非递减排序,再依次判断排序数组和原数组每一位上的模2余数是否都相同即可

void solve(){
	int n;
	cin >> n;
	vector<int> a(n), b(n);
	for(int i = 0; i < n; ++ i){
		cin >> a[i];
		b[i] = a[i];
	}
	sort(b.begin(), b.end());
	for(int i = 0; i < n; ++ i){
		if(a[i] % 2 != b[i] % 2){
			cout << "NO\n";
			return ;
		}
	}
	cout << "Yes\n";
	return ;
}

C :最简单的方法,因为只需要判断能否抵达终点,那么先找起点出去有没有 k 块起点颜色,找到 k 块后再开始统计后面的颜色,最后判断终点的颜色的砖块数是否多于 k 块即可

void solve(){
	int n, k;
	cin >> n >> k;
	vector<int> c(n);
	map<int,int> q;
	bool f = false;
	for(int i = 0; i < n; ++ i){
		cin >> c[i];
		if(c[i] == c[0] && !f){
			++ q[c[0]];
			if(q[c[0]] == k){
				f = true;
			}
		}else if(f){
			++ q[c[i]];
		}
	}
	if(q[c[n - 1]] >= k) cout << "YES\n";
	else cout << "NO\n";
	return ;
}

D. Prefix Permutation Sums

题意
判断给定的长为n - 1数组,是否为某个 1 ~ n 的序列的前缀和数组漏了一个数形成的数组

思路
就是判断能否变回去,毫无感情的判断机器
统计给定前缀和数组的差分数组得到所有的目前有的 n - 1 个数,那么如果他是,要么它仅缺失了首尾的前缀数字,或者说,就是重复了一个数或者大于n一个数且这个数为整体缺失的两个数之和

代码

//>>>Qiansui
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x, y, sizeof(x))
#define debug(x) cout << #x << " = " << x << '\n'
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << '\n'
//#define int long long

using namespace std;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<ull, ull> pull;
typedef pair<double, double> pdd;
/*

*/
const int maxm = 2e5 + 5, inf = 0x3f3f3f3f, mod = 998244353;

void solve(){
	int n;
	cin >> n;
	vector<ll> a(n + 1, 0), b;
	vector<bool> vis(n + 1, false);
	for(int i = 1; i < n; ++ i){
		cin >> a[i];
		ll t = a[i] - a[i - 1];
		if(t <= n){
			if(vis[t]) b.push_back(t);
			vis[t] = true;
		}else b.push_back(t);
	}
	vector<ll> ne;
	for(int i = 1; i <= n; ++ i){
		if(!vis[i]) ne.push_back(i);
	}
	if(ne.size() == 1 && b.size() == 0){
		cout << "YES\n";
		return ;
	}
	if(ne.size() == 2 && b.size() == 1){
		if(b[0] == ne[0] + ne[1]){
			cout << "YES\n";
			return ;
		}
	}
	cout << "NO\n";
	return ;
}

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

E. Nastya and Potions

题意
题面有点长,慢慢读即可
有 n 种药剂,每种药剂在市面上都有一个价格 c,其中 k 种你自己就有货,不需要买。每一种药剂都有一个合成配方,没有合成配方的药剂只能买。问你所有获得每种药剂的花费(要么买,要么合成)

思路

  • 对于已经有的药剂,花费为0
  • 对于剩下的药剂,只需要比较买的价格和合成的价格哪个便宜就选则哪种方式

注意到药剂合成的相互牵制关系,所以我们将合成关系抽象成 DAG,再利用 toposort 处理先后顺序即可。简单来说就是 toposort 的简单题

代码

//>>>Qiansui
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x, y, sizeof(x))
#define debug(x) cout << #x << " = " << x << '\n'
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << '\n'
//#define int long long

using namespace std;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<ull, ull> pull;
typedef pair<double, double> pdd;
/*

*/
const ll maxm = 2e5 + 5, inf = 0x3f3f3f3f3f3f3f3f, mod = 998244353;

void solve(){
	int n, k;
	cin >> n >> k;
	vector<ll> c(n + 1), p(n + 1, 0);
	for(int i = 1; i <= n; ++ i){
		cin >> c[i];
	}
	ll t;
	for(int i = 1; i <= k; ++ i){
		cin >> t;
		c[t] = 0;	//这种药剂花费为 0
	}
	vector<int> mix[n + 1], in(n + 1, 0);
	for(int i = 1; i <= n; ++ i){
		int m;
		cin >> m;
		while(m --){
			cin >> t;
			mix[t].push_back(i);
			++ in[i];
		}
	}
	queue<int> q;
	for(int i = 1; i <= n; ++ i){
		if(in[i] == 0){
			p[i] = c[i];	//这种药剂只能买
			q.push(i);
		}
	}
	while(q.size()){
		t = q.front(); q.pop();
		c[t] = min(c[t], p[t]);		//购买与合成取最小
		for(auto x : mix[t]){
			-- in[x];
			if(in[x] == 0) q.push(x);
			p[x] += c[t];	//向下更新合成花费
		}
	}
	for(int i = 1; i <= n; ++ i){
		cout << c[i] << " \n"[i == n];
	}
	return ;
}

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


F. Lisa and the Martians

题意
给你长为 n 的数组 a [1 ~ n] ,数组 a 的所有元素均为非负整数且严格小于 \(2^k\),这样的数被称为火星数字
让你从数组 a 中选择两个数,下标为 i 和 j ,再确定一个火星数字 x ,使得式子$(a_i \oplus x) \& (a_j \oplus x) $最大

思路
设 $y = (a_i \oplus x) \& (a_j \oplus x) $
x 异或的作用就是决定 $a_i $ 和 $a_j $ 的同一位翻转与否,之后再进行位与操作。那么要想最后的值最大,我们需要让每一位都尽可能是 1。对于二进制中的某一位 m,当$a_{im} = a_{jm} = b $时,应取 $x_m = \overline{b} $,此时 \(y_m = 1\) ;当$a_{im} \ne a_{jm} $时,无论怎么取 \(x_m\),$y_m $ 均等于 0
所以,我们需要让 $a_i $ 和 $a_j $ 二进制中相同的位数尽可能多,也就是两者异或和尽可能小。有个结论:一个数组选两个数的异或和最小值出现在排序之后的相邻数之间。在知道这个结论之后,$a_i $ 和 $a_j $ 已经确定,至于数 x,我们只需要让 $a_i $ 和 $a_j $ 二进制相同的位上让 x 取反,其余位随意即可。一种可能的方式就是 $x = (2^k - 1) \oplus (a_i | a_j ) $ 或者$x = (2^k - 1) \oplus (a_i \& a_j ) $

也可以利用trie数求解异或最小。题目的关键在于发现异或最小这一隐藏条件。

代码

//>>>Qiansui
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x, y, sizeof(x))
#define debug(x) cout << #x << " = " << x << '\n'
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << '\n'
//#define int long long

using namespace std;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<ull, ull> pull;
typedef pair<double, double> pdd;
/*

*/
const int maxm = 2e5 + 5, inf = 0x3f3f3f3f, mod = 998244353;

void solve(){
	int n, k, t;
	cin >> n >> k;
	vector<pii> a(n);
	for(int i = 0; i < n; ++ i){
		cin >> t;
		a[i] = {t, i + 1};
	}
	sort(a.begin(), a.end());
	int ans = (1 << k), x, y, z;
	for(int i = 1; i < n; ++ i){
		int t = a[i].first ^ a[i - 1].first;
		if(t < ans){
			ans = t;
			y = a[i].second;
			z = a[i - 1].second;
			x = ((1 << k) - 1) ^ (a[i].first | a[i].first);
		}
	}
	cout << y << ' ' << z << ' ' << x  << '\n';
	return ;
}

signed main(){
	ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
	int _ = 1;
	cin >> _;
	while(_ --){
		solve();
	}
	return 0;
}
posted on 2023-07-26 20:24  Qiansui  阅读(31)  评论(0编辑  收藏  举报