CF653C 美丽序列
1 CF653C 美丽序列
2 题目描述
时间限制 \(2s\) | 空间限制 \(256M\)
生活就像美丽的序列一样上下起伏,满足下面两个条件的序列 \(t_1,t_2,...,t_n\) 是美丽的:
- 对于每个奇数的 \(i < n\) 都有 \(t_i < t\)\(_i\)\(_+\)\(_1\)
- 对于每个偶数的 \(i < n\) 都有 \(t_i > t\)\(_i\)\(_+\)\(_1\)
例如,序列 \((2,8),(1,5,1)\) 和 \((2,5,1,100,99,120)\) 是美丽的,而 \((1,1),(1,2,3)\) 和 \((2,5,3,2)\) 就不是。
狗熊 \(Limak\) 有一个正整数 \(t_1,t_2,...,t_n\) 的序列。这个序列现在不美丽,狗熊 \(Limak\) 想通过单次交换来修复它。为了变成一个美丽序列,他要选择两个下标 \(i < j\) 来交换 \(t_i\) 和 \(t_j\)。计算总共有多少种方法实现,如果交换元素下标不同则认为这是不同的方法。
3 题解
我们容易想到,如果不符合条件的地方超过 \(6\) 处,那么我们肯定无法将序列改正:假如我们这次交换的是两个不符合要求的数,最多可以让这两个数及它们左右各 \(2\) 个共 \(6\) 个数符合条件。因此,我们在一开始记录下所有的不合法的位置和不合法的数的个数,如果不合法的个数大于 \(6\),那么直接输出 \(6\) 即可。
我们发现:如果我们想要经可能地减少不符合条件的数的个数,那么我们交换时交换的两个数中至少有一个不符合条件的数。那么,我们就可以考虑遍历所有不符合条件的数,并枚举与其交换的数。将当前不符合条件的数和从第 \(1\) 个数到第 \(n\) 个数交换之后,检查是否所有数都符合条件,如果符合的话,当前方案就是一种符合条件的方案,记录答案的 \(ans + 1\)。
这里需要注意的是,有一些情况是,我们当前枚举的与我们的不符合条件的数交换的那个数也是不符合条件的。在这些情况下,这些不符合条件的数在自己找的时候未免就会和我们当前枚举的不符合条件的数再交换一次,这样答案就会重复。为了避免计算重复,我们可以选择强制让两个符合条件的数交换时第一个数的坐标大于第二个数,这样就只会统计一遍了。
4 代码(空格警告):
#include <iostream>
using namespace std;
const int N = 15e4+10;
const int INF = 0x3f3f3f3f;
int n, cnt, ans;
int t[N], w[N];
bool f[N], flag;
bool check(int x)
{
if (x % 2 && t[x] < t[x+1] && t[x] < t[x-1]) return 1;
if (!(x % 2) && t[x] > t[x+1] && t[x] > t[x-1]) return 1;
return 0;
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i++) cin >> t[i];
t[0] = INF; t[n+1] = INF * (n%2);
for (int i = 1; i <= n; i++)
{
if (!check(i))
{
w[++cnt] = i;
f[i] = 1;
}
}
if (cnt > 6)
{
cout << 0;
return 0;
}
for (int i = 1; i <= cnt; i++)
{
for (int j = 1; j <= n; j++)
{
if (f[j] && j <= w[i]) continue;
swap(t[w[i]], t[j]);
flag = check(j);
for (int k = 1; k <= cnt; k++) flag &= check(w[k]);
ans += flag;
swap(t[w[i]], t[j]);
}
}
cout << ans;
return 0;
}