Codeforces Round #669 (Div. 2) A - D 解题报告
题目链接
A. Ahahahahahahahaha
题意:
给定长度为 \(n\) 的 \(0, 1\) 数组,\(n\) 为偶数,你最多可以删去 \(\frac{n}{2}\) 个数,使得结果数组的奇数位和等于偶数位和,输出结果数组。
思路:
计算出 \(0\) 和 \(1\) 的个数,谁多就输出谁。
注:\(1\) 多的情况要判断奇偶。
代码:
/*
* @Author : nonameless
* @Date : 2020-09-08 22:22:23
* @LastEditors : nonameless
* @LastEditTime : 2020-09-08 22:52:09
*/
#include <bits/stdc++.h>
#define x first
#define y second
#define pb push_back
#define sz(x) (int)x.size()
#define all(x) x.begin(), x.end()
using namespace std;
typedef long long ll;
typedef pair<ll, ll> PLL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
const double eps = 1e-6;
const double PI = acos(-1.0);
const int INF = 0x3f3f3f3f;
const ll LNF = 0x3f3f3f3f3f3f;
inline int gcd(int a, int b) { return b ? gcd(b, a % b) : a; }
inline ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
inline int lcm(int a, int b) { return a * b / gcd(a, b); }
const int N = 1e3 + 10;
int a[N];
int main(){
int t; cin >> t;
while(t --){
int n; cin >> n;
for(int i = 1; i <= n; i ++) scanf("%d", a + i);
int x = n >> 1;
int one = 0, zero = 0;
for(int i = 1; i <= n; i ++)
if(a[i] == 1) one ++;
else zero ++;
if(one > x){
if(one & 1) one --;
cout << one << endl;
for(int i = 1; i <= one; i ++) printf("1 ");
puts("");
} else{
cout << zero << endl;
for(int i = 1; i <= zero; i ++) printf("0 ");
puts("");
}
}
return 0;
}
B. Big Vova
题意:
给定一个序列 \(a\),序列 \(b\) 是 \(a\) 的一个排列,定义 \(c_i = gcd(b_1,...,b_i)\)。
要求求得的 \(c\) 的字典序最大,求序列 \(b\)。
思路:
看数据范围就可以知道直接暴力即可。
首先很明显的是 \(b_1 = max(a_1,a_2,...,a_n)\),根据这个找下去即可。
代码:
/*
* @Author : nonameless
* @Date : 2020-09-08 22:59:37
* @LastEditors : nonameless
* @LastEditTime : 2020-09-08 23:24:08
*/
#include <bits/stdc++.h>
#define x first
#define y second
#define pb push_back
#define sz(x) (int)x.size()
#define all(x) x.begin(), x.end()
using namespace std;
typedef long long ll;
typedef pair<ll, ll> PLL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
const double eps = 1e-6;
const double PI = acos(-1.0);
const int INF = 0x3f3f3f3f;
const ll LNF = 0x3f3f3f3f3f3f;
inline int gcd(int a, int b) { return b ? gcd(b, a % b) : a; }
inline ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
inline int lcm(int a, int b) { return a * b / gcd(a, b); }
const int N = 1e3 + 10;
int a[N], b[N];
map<int, int> mp;
int main(){
int t; cin >> t;
while(t --){
int n; cin >> n;
for(int i = 1; i <= n; i ++) scanf("%d", a + i);
for(int i = 1; i <= n; i ++) mp[a[i]] ++;
b[1] = *max_element(a + 1, a + n + 1);
int idx = 1;
int x = b[1];
mp[b[1]]--;
while(idx < n){
int tmp = 1, val;
for(int i = 1; i <= n; i ++){
if(mp[a[i]]){
if(gcd(x, a[i]) > tmp){
tmp = gcd(x, a[i]);
val = a[i];
}
}
}
if(tmp == 1) break;
b[++idx] = val;
mp[val] --;
x = tmp;
}
if(idx < n){
for(int i = 1; i <= n; i ++){
if(mp[a[i]]) b[++idx] = a[i];
}
}
for(int i = 1; i <= n; i ++) printf("%d ", b[i]);
puts("");
mp.clear();
}
return 0;
}
C. Chocolate Bunny
题意:
交互题。
求一个未知的全排列 \(a\)。
可以询问 \(2\times n\) 次,每次给出两个不同的索引 \(i\) 和 \(j\),会返回 \(a_i \% a_j\) 的值。
思路:
询问 \(i, j\),我们假设 \(a_i < a_j\)。
显然 \(a_i \% a_j = x = a_i\)。
再询问 \(j, i\):
显然 \(a_j \% a_i = y < a_i\)
那么我们显然 \(a_i = max(x, y)\)。
每询问两次可以确定一个位置的值,符合要求。
代码:
/*
* @Author : nonameless
* @Date : 2020-09-08 23:25:35
* @LastEditors : nonameless
* @LastEditTime : 2020-09-08 23:45:28
*/
#include <bits/stdc++.h>
#define x first
#define y second
#define pb push_back
#define sz(x) (int)x.size()
#define all(x) x.begin(), x.end()
using namespace std;
typedef long long ll;
typedef pair<ll, ll> PLL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
const double eps = 1e-6;
const double PI = acos(-1.0);
const int INF = 0x3f3f3f3f;
const ll LNF = 0x3f3f3f3f3f3f;
inline int gcd(int a, int b) { return b ? gcd(b, a % b) : a; }
inline ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
inline int lcm(int a, int b) { return a * b / gcd(a, b); }
const int N = 1e4 + 10;
int arr[N];
int main(){
int n; cin >> n;
if(n == 1) puts("! 1");
else{
map<int, int> mp;
int x = 1;
int a, b;
for(int i = 2; i <= n; i ++){
cout << "? " << x << " " << i << endl;
cin >> a;
cout << "? " << i << " " << x << endl;
cin >> b;
if(a < b){
arr[i] = b;
mp[b] = 1;
} else{
arr[x] = a;
mp[a] = 1;
x = i;
}
}
for(int i = 1; i <= n; i ++){
if(mp[i] == 1) continue;
arr[x] = i;
break;
}
cout << "!";
for(int i = 1; i <= n; i ++) cout << " " << arr[i];
puts("");
}
return 0;
}
D. Discrete Centrifugal Jumps
题意:
有 \(n\) 个柱子,第 \(i\) 个柱子的高度为 \(h_i\),你满足以下条件之一就可以从 \(i\) 跳到 \(j\):
- \(j = i + 1\)
- \(max(h_{i+1},...,h_{j-1}) < min(h_i, h_j)\)
- \(max(h_i, h_j) < min(h_{i+1},...,h_{j-1})\)
问从第一个柱子跳到第 \(n\) 个柱子的最小步数?
思路:
考虑线性 \(dp\),设 \(f[i]\):跳到第 \(i\) 个柱子的最小步数
转移方程:\(f[j] = min(f[j], f[i] + 1)\)(如果 \(i\) 可以跳到 \(j\) )。
第一种情况不做考虑,此时我们只考虑第二种情况,第三种类似。
对于 \(i\) 来说,\(j\) 就是大于等于他,且离他最近的那个柱子(\(i < j\)),那么此时 \(i\) 可以跳到 \(j\),显然这个性质可以单调栈来解决。
不理解代码的可以用这组样例来理解第二种情况:\(4,3, 2, 1, 5\).
注:
- 单调递增栈:从栈顶到栈底递增
- 单调递减栈:从栈顶到栈底递减
代码:
/*
* @Author : nonameless
* @Date : 2020-09-09 15:10:43
* @LastEditors : nonameless
* @LastEditTime : 2020-09-09 15:37:44
*/
#include <bits/stdc++.h>
#define x first
#define y second
#define pb push_back
#define sz(x) (int)x.size()
#define all(x) x.begin(), x.end()
using namespace std;
typedef long long ll;
typedef pair<ll, ll> PLL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
const double eps = 1e-6;
const double PI = acos(-1.0);
const int INF = 0x3f3f3f3f;
const ll LNF = 0x3f3f3f3f3f3f;
inline int gcd(int a, int b) { return b ? gcd(b, a % b) : a; }
inline ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
inline int lcm(int a, int b) { return a * b / gcd(a, b); }
const int N = 3e5 + 10;
int h[N], f[N];
stack<int> in, de;
int main(){
int n; cin >> n;
memset(f, INF, sizeof f);
f[1] = 0;
auto update = [&](stack<int> &s, int idx){
if(!s.empty()){
f[idx] = min(f[idx], f[s.top()] + 1);
if(h[s.top()] == h[idx]) s.pop();
}
s.push(idx);
};
for(int i = 1; i <= n; i ++){
scanf("%d", h + i);
while(!de.empty() && h[i] < h[de.top()]){ // 单调递减栈 对应第三种情况
f[i] = min(f[i], f[de.top()] + 1);
de.pop();
}
update(de, i);
while(!in.empty() && h[i] > h[in.top()]){ // 单调递增栈 对应第二种情况
f[i] = min(f[i], f[in.top()] + 1);
in.pop();
}
update(in, i);
}
cout << f[n] << endl;
return 0;
}