A. Greatest Convex
给出数字k,输出最大的x,使得x满足大于等于1小于k,且x! + (x - 1)!是k的倍数。
思路:提取公因式得到,(x + 1) * (x - 1)!,由题意知,x + 1可以是k,故x最大是k - 1且一定存在。
AC Code:
#include <bits/stdc++.h>
typedef long long ll;
#define INF 0x3f3f3f3f
const int N = 2e5 + 5;
int t, n;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
std::cin >> t;
while(t --) {
std::cin >> n;
std::cout << n - 1 << '\n';
}
return 0;
}
B. Quick Sort
给出排列p,每次操作可以选择k个不同的数,将其从排列中拿出并升序排序后放到数组末尾,问最少经过几次操作使得数组升序排序。
思路:对于从1开始的,我们是可以不动的,只计算剩余相对位置不对的数字即可。
AC Code:
#include <bits/stdc++.h>
typedef long long ll;
#define INF 0x3f3f3f3f
const int N = 2e5 + 5;
int t, n, k;
int a[N];
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
std::cin >> t;
while(t --) {
std::cin >> n >> k;
int num = 1;
for(int i = 1; i <= n; i ++) {
std::cin >> a[i];
if(a[i] == num)
num ++;
}
if(n == num - 1) {
std::cout << 0 << '\n';
continue;
}
int ans = (int)ceil((n - num + 1) * 1.0 / (1.0 * k));
std::cout << ans << '\n';
}
return 0;
}
C. Elemental Decompress
对于两个长度相等的排列p,q,给出数组c,表示相同位置p与q的最大值,构造满足条件的排列p和q,或者若是不存在满足条件的排列,输出-1。
思路:对于在数组中出现一次的数字,我们可以将对应位置的两个数都赋值为该数;剩余的必定是出现0次和出现两次的数字,且两种数字必定数量相等,出现两次的数字代替了本该出现一次的数字。这样,依照此依据赋值即可,注意大小顺序和no的情况。
AC Code:
#include <bits/stdc++.h>
typedef long long ll;
const int N = 2e5 + 5;
int t, n;
int a[N], p[N], q[N], h[N], tmp[N];
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
std::cin >> t;
while(t --) {
std::cin >> n;
std::map<int, int> mp;
for(int i = 1; i <= n; i ++) {
p[i] = q[i] = h[i] = tmp[i] = 0;
std::cin >> a[i];
mp[a[i]] ++;
}
bool flag = true;
for(auto [x, y] : mp) {
if(y > 2) {
flag = false;
break;
}
}
if(!flag) {
std::cout << "NO" << '\n';
continue;
}
std::vector<int> cnt0, cnt2;
for(int i = 1; i <= n; i ++) {
if(!mp[i])
cnt0.push_back(i);
else if(mp[i] == 2)
cnt2.push_back(i);
}
std::sort(cnt0.begin(), cnt0.end());
std::sort(cnt2.begin(), cnt2.end());
if(cnt0.size() != cnt2.size()) {
std::cout << "NO" << '\n';
continue;
}
for(int i = 0; i < (int)cnt0.size(); i ++) {
if(cnt0[i] < cnt2[i])
h[cnt2[i]] = cnt0[i];
else {
flag = false;
break;
}
}
if(!flag) {
std::cout << "NO" << '\n';
continue;
}
for(int i = 1; i <= n; i ++) {
if(mp[a[i]] == 1)
p[i] = q[i] = a[i];
}
for(int i = 1; i <= n; i ++) {
tmp[a[i]] ++;
if(tmp[a[i]] == 1 && mp[a[i]] == 2)
p[i] = a[i], q[i] = h[a[i]];
if(tmp[a[i]] == 2 && mp[a[i]] == 2)
p[i] = h[a[i]], q[i] = a[i];
}
std::cout << "YES" << '\n';
for(int i = 1; i <= n; i ++) {
std::cout << p[i] << " \n"[i == n];
}
for(int i = 1; i <= n; i ++) {
std::cout << q[i] << " \n"[i == n];
}
}
return 0;
}
D. Lucky Permutation
给出一个排列p,每次可以将两个数字交换位置,问最少经过几次操作使得排列中仅存在一个逆序对。
思路:置换环问题。对于最后会达成的序列,我们可以将其先不管逆序对,即先置换成升序排列的数组,这样在序列中会形成若干个环,通过对环的置换使得数字回到原来的位置。在这种情况下,操作次数是n - cnt,cnt为环的个数。而对于这一个逆序对,很容易想到可以在已经排好的升序序列中交换任意相邻两数。但是,如果在某一个环中存在相邻的两个数,我们可以通过减少一次在该环置换的次数,使得置换完后存在一个逆序对,具体看代码。
AC Code:
#include <bits/stdc++.h>
typedef long long ll;
const int N = 2e5 + 5;
int t, n;
int a[N];
bool flag, vis[N];
std::map<int, bool> mp;
void DFS(int u) {
if(vis[u])
return;
vis[u] = true;
mp[u] = 1;
int v = a[u];
if(mp[u - 1] || mp[u + 1])
flag = 1;
DFS(v);
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
std::cin >> t;
while(t --) {
std::cin >> n;
for(int i = 1; i <= n; i ++) {
std::cin >> a[i];
vis[i] = 0;
}
int cnt = 0;
flag = false;
for(int i = 1; i <= n; i ++) {
if(!vis[i]) {
cnt ++;
mp.clear();
DFS(i);
}
}
if(flag)
std::cout << n - cnt - 1 << '\n';
else
std::cout << n - cnt + 1 << '\n';
}
return 0;
}