A. Penchick and Modern Monument
思路
保留原来 n n n 个数中数量最多的一种数字,然后修改其他数使得序列不递减。
复杂度
时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n)
代码实现
cpp
// Problem: A. Penchick and Modern Monument
// Contest: Codeforces - Codeforces Round 987 (Div. 2)
// URL: https://codeforces.com/contest/2031/problem/A
// Memory Limit: 256 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5e4 + 5, M = 17;
int n;
int a[N];
void solve()
{
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
int ma = 0;
for (int i = 1; i <= n; i++) {
int j = i;
while (j <= n && a[i] == a[j]) {
j++;
}
ma = max(ma, j - i);
i = j - 1;
}
int ans = n - ma;
cout << ans << '\n';
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int T = 1;
cin >> T;
while (T--) {
solve();
}
}
B. Penchick and Satay Sticks
思路
赛时思路:
发现形如 3 , 2 , 1 3,2,1 3,2,1 这样子安排, 1 1 1 是无法移动到第一个位置的,那么 1 1 1 开始时要么在第一个位置,要么在第二个位置,然后 2 2 2 在第一个位置。
当把 1 1 1 放到第一个位置的时候, 2 2 2 就变成了剩下 n − 1 n-1 n−1 个数的第一个数,也是进行相同的判断。
因此,可以从左到右遍历,如果 p i = i p_i = i pi=i,那么当前最小的数就安排好了位置,否则 p i ≠ i p_i \ne i pi=i,如果 p i + 1 ≠ i p_{i+1} \ne i pi+1=i 无解,否则交换 p i , p i + 1 p_i,p_{i+1} pi,pi+1,然后接着判断 p i + 1 p_{i+1} pi+1,直到判断完所有数为止。
题解思路:
如果数字 i i i 初始位置与 最后的位置 i i i 的距离超过 1 1 1,那么就无法把数字 i i i 移动到最后的位置。
因此,若 ∣ p i − i ∣ |p_i - i| ∣pi−i∣ 的最大值是大于 1 1 1 的,那么无解,否则有解。
复杂度
时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n)
代码实现
cpp
// Problem: B. Penchick and Satay Sticks
// Contest: Codeforces - Codeforces Round 987 (Div. 2)
// URL: https://codeforces.com/contest/2031/problem/B
// Memory Limit: 256 MB
// Time Limit: 1500 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 5, M = 17;
int n;
int a[N];
void solve()
{
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 1; i <= n; i++) {
if (a[i] != i) {
if (i + 1 <= n && a[i + 1] == a[i] - 1) {
swap(a[i], a[i + 1]);
}
}
if (a[i] != i) {
cout << "NO\n";
return;
}
}
cout << "YES\n";
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int T = 1;
cin >> T;
while (T--) {
solve();
}
}
C. Penchick and BBQ Buns
思路
如果 n n n 为偶数,那么可以相邻的两个数两两作为一组,距离为 1 1 1 恰好是一个平方数。
如果 n n n 为奇数,手玩发现有解的最小奇数为 27 27 27。
因为不超过 27 27 27 的范围内有平方数 9 + 16 = 25 9+16=25 9+16=25,若答案序列为 a a a,可以令 a 1 , a 10 , a 26 a_1,a_{10},a_{26} a1,a10,a26 作为一组, a 11 , a 27 a_{11},a_{27} a11,a27 作为一组,然后剩下的数与相邻的数从左到右两两作为一组。
复杂度
时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n)
代码实现
cpp
// Problem: C. Penchick and BBQ Buns
// Contest: Codeforces - Codeforces Round 987 (Div. 2)
// URL: https://codeforces.com/contest/2031/problem/C
// Memory Limit: 256 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5e4 + 5, M = 17;
void solve()
{
int n;
cin >> n;
if (n % 2 == 0) {
int p = 1;
for (int i = 1; i <= n; i += 2) {
cout << p << ' ' << p << ' ';
p++;
}
cout << '\n';
} else {
if (n < 27) {
cout << "-1\n";
return;
}
int p = 1;
vector<int> a(n + 1);
a[1] = a[10] = a[26] = p++;
for (int i = 2; i < 10; i += 2) {
a[i] = a[i + 1] = p++;
}
a[11] = a[27] = p++;
for (int i = 12; i < 26; i += 2) {
a[i] = a[i + 1] = p++;
}
for (int i = 28; i <= n; i += 2) {
a[i] = a[i + 1] = p++;
}
for (int i = 1; i <= n; i++) {
cout << a[i] << ' ';
}
cout << '\n';
}
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int T = 1;
cin >> T;
while (T--) {
solve();
}
}
D. Penchick and Desert Rabbit
思路
如果区间 [ l , r ] [l,r] [l,r] 中的最大高度为 a l a_l al,那么 [ l , r ] [l,r] [l,r] 中的任意一个位置出发,可以向左跳到 l l l 位置,取得高度 a l a_l al。
如果区间 [ l 1 , r 1 ] [l_1,r_1] [l1,r1] 满足 l 1 > r l_1>r l1>r, a l 1 a_{l_1} al1 为 [ l 1 , r 1 ] [l_1,r_1] [l1,r1] 的最大值,且 a l 1 > a l a_{l_1}>a_l al1>al, [ l 1 , r 1 ] [l_1,r_1] [l1,r1] 的最小值小于 a l a_l al,那么从 [ l , r ] [l,r] [l,r] 中的任意位置出发,可以向左跳到 l l l 位置,然后再跳到 [ l 1 , r 1 ] [l_1,r_1] [l1,r1] 的最小值位置,在跳到 l 1 l_1 l1 位置,取得更大的高度 a l 1 a_{l_1} al1。
通过上面的思考,可以发现,如果 a i a_i ai 为 [ 1 , n ] [1,n] [1,n] 中的下标最小的最大值。(就是说如果存在多个最大值, i i i 是最大值下标中的最小下标。),那么从 [ i , n ] [i,n] [i,n] 中的任意位置出发,都可以跳到最大高度 a i a_i ai。
如果 [ 1 , i − 1 ] [1,i-1] [1,i−1] 中的下标最小的最大值为 a j a_j aj,那么从 [ j , i − 1 ] [j,i-1] [j,i−1] 中的任意位置出发,可以跳到高度 a j a_j aj。
如果要跳到高度 a i a_i ai,那么需要 [ i , n ] [i,n] [i,n] 中存在一个高度 a k < a j a_k < a_j ak<aj,也就是说 [ i , n ] [i,n] [i,n] 中的最小高度要小于 a j a_j aj,才能使得从 [ j , i − 1 ] [j,i-1] [j,i−1] 中的任意位置出发,能够跳到高度 a i a_i ai,否则能跳到的最大高度就是 a j a_j aj。
继续向左找下标最小的最大值,这样子会划分出若干个连续的子区间,满足上一个子区间的右端点加一为下一个子区间的左端点,子区间的左端点的值为整个子区间的最大值,且子区间的最大值是从右到左递减的。
(要快速找下标最小的最大值,可以先预处理出 s t st st 表,然后用 s t st st 表进行查询,下面找区间内的最小值同样也是可以用 s t st st 表。)
对于在同一个子区间内的位置,最终都能达到的高度都是相同的。
对于一个子区间 [ l , r ] [l,r] [l,r],如果右边存在一个子区间 [ l 1 , r 1 ] [l_1,r_1] [l1,r1]的最小高度小于子区间 [ l , r ] [l,r] [l,r] 的最大高度,那么子区间 [ l , r ] [l,r] [l,r] 内的位置可以达到的最大高度就可以为子区间 [ l 1 , r 1 ] [l_1,r_1] [l1,r1] 能达到的最大高度。
可能右边会存在多个子区间的最小高度,小于子区间 [ l , r ] [l,r] [l,r] 的最大高度,这些子区间中的最大高度的最大值,就是区间 [ l , r ] [l,r] [l,r] 内的位置可以达到的最大高度。
直接遍历右边子区间,看哪些子区间的最小高度大于区间 [ l , r ] [l,r] [l,r] 的最大高度,显然是会超时的。
可以用权值线段树进行维护,维护的单点 x x x 的数值,表示右边子区间的最小高度为 x x x 的能达到的最大高度。
当遍历到一个子区间 [ l , r ] [l,r] [l,r] 时,就可以查询最小高度在 [ 1 , a l − 1 ] [1,a_l-1] [1,al−1] 范围内的,右边的子区间能达到的最大高度,与 a l a_l al 比较取最大值,即是 [ l , r ] [l,r] [l,r] 能达到的最大高度。
遍历完子区间 [ l , r ] [l,r] [l,r] 后,就可以新该子区间最小高度的对应数值,然后求解区间 [ 1 , l − 1 ] [1,l-1] [1,l−1]。
复杂度
时间复杂度 O ( n log n ) O(n\log n) O(nlogn),空间复杂度 O ( n log n ) O(n \log n) O(nlogn)
代码实现
cpp
// Problem: D. Penchick and Desert Rabbit
// Contest: Codeforces - Codeforces Round 987 (Div. 2)
// URL: https://codeforces.com/contest/2031/problem/D
// Memory Limit: 256 MB
// Time Limit: 3000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5e5 + 5, M = 20;
int n;
int f[N][M], g[N][M];
int a[N], ans[N];
int query_st(int l, int r, int op)
{
int k = log2(r - l + 1);
if (!op) {
int id1 = f[l][k];
int id2 = f[r - (1 << k) + 1][k];
return a[id2] > a[id1] ? id2 : id1;
} else {
int id1 = g[l][k];
int id2 = g[r - (1 << k) + 1][k];
return a[id2] < a[id1] ? id2 : id1;
}
}
struct {
int l, r, v;
} tr[4 * N];
void build(int u, int l, int r)
{
tr[u].l = l, tr[u].r = r, tr[u].v = 0;
if (l != r) {
int mid = (l + r) >> 1;
build(u * 2, l, mid);
build(u * 2 + 1, mid + 1, r);
}
}
void pushup(int u)
{
tr[u].v = max(tr[u * 2].v, tr[u * 2 + 1].v);
}
void modify(int u, int p, int x)
{
if (tr[u].l == tr[u].r) {
tr[u].v = max(tr[u].v, x);
return;
}
int mid = (tr[u].l + tr[u].r) >> 1;
if (p <= mid)
modify(u * 2, p, x);
else
modify(u * 2 + 1, p, x);
pushup(u);
}
int query(int u, int l, int r)
{
if (l <= tr[u].l && tr[u].r <= r)
return tr[u].v;
int res = 0;
int mid = (tr[u].l + tr[u].r) >> 1;
if (l <= mid)
res = max(res, query(u * 2, l, r));
if (r > mid)
res = max(res, query(u * 2 + 1, l, r));
return res;
}
void solve()
{
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
ans[i] = 0;
}
for (int j = 0; j < M; j++) {
int step = 1 << (j - 1);
for (int i = 1; i + (1 << j) - 1 <= n; i++) {
if (!j) {
f[i][j] = g[i][j] = i;
} else {
f[i][j] = f[i][j - 1];
if (a[f[i + step][j - 1]] > a[f[i][j]]) {
f[i][j] = f[i + step][j - 1];
}
g[i][j] = g[i][j - 1];
if (a[g[i + step][j - 1]] < a[g[i][j]]) {
g[i][j] = g[i + step][j - 1];
}
}
}
}
build(1, 1, n);
int r = n;
while (r >= 1) {
int l = query_st(1, r, 0);
int v = a[l];
if (a[l] > 1) {
v = max(query(1, 1, a[l] - 1), v);
}
for (int i = l; i <= r; i++) {
ans[i] = v;
}
int w = a[query_st(l, r, 1)];
modify(1, w, v);
r = l - 1;
}
for (int i = 1; i <= n; i++) {
cout << ans[i] << ' ';
}
cout << '\n';
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int T = 1;
cin >> T;
while (T--) {
solve();
}
}