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;
}