题目描述
难度分:1400
输入T(≤104)表示T组数据。所有数据的n之和≤2×105。
每组数据输入n(3≤n≤2×105)和长为n的数组a(1≤a[i]≤109)。
你需要从a中恰好删除一个数,得到长为n−1的数组a′。然后生成一个长为n−2的数组b,其中b[i]=GCD(a′[i],a′[i+1])。
你需要让数组b是非降序列,即b[i]≤b[i+1]。
能否做到?输出YES
或NO
。
输入样例
12
6
20 6 12 3 48 36
4
12 6 3 4
3
10 12 3
5
32 16 8 4 2
5
100 50 2 10 20
4
2 4 8 1
10
7 4 6 2 4 5 1 4 2 8
7
5 9 6 8 5 9 2
6
11 14 8 12 9 3
9
5 7 3 10 6 3 12 6 3
3
4 2 4
8
1 6 11 12 6 12 3 6
输出样例
YES
NO
YES
NO
YES
YES
NO
YES
YES
YES
YES
YES
算法
前后缀分解
先做个前后缀分解的预处理,预处理得到pre和suf两个数组。pre[i]表示a的前缀[1,i]按照题中构造方法能够构造出来的b的最长不递减后缀长度;suf[i]表示a的后缀[i,n]按照题中构造方法能够构造出来的b的最长不递减前缀长度。以pre[i]为例进行说明,令p=gcd(a[i−2],a[i−1]),c=gcd(a[i−1],a[i]),只要p≤c成立,说明在前缀[1,i−1]上构造出来的b(以p结尾)在后面添加一个c能让b的非递减长度更长,pre[i]=pre[i−1]+1;否则pre[i]的值是什么就无关紧要了,因为a[i]被删除肯定满足不了要求。
然后枚举删除的元素a[i],只要满足gcd(a[i−2],a[i−1])≤gcd(a[i−1],a[i+1])≤gcd(a[i+1],a[i+2]),且pre[i−1]=i−2,suf[i+1]=n−i−1。说明删除a[i]构造出来的b是单调不减的,只要存在一个这样的i就行。但要注意在i=1,2,n−1,n时以上判断条件会出现数组越界,可以特判解决,但想起来费劲,不如枚举这四种情况,检查构造出来的b是不是单调不减,反正也只需要4次O(n)。
复杂度分析
时间复杂度
预处理出pre和suf的时间复杂度为O(n),枚举删除位置i计算答案的时间复杂度也是O(n),所以整个算法的时间复杂度就是O(n)。
空间复杂度
pre和suf的空间消耗是线性的,在枚举i=1,2,n−1,n四种情况时需要确实地构造出a′和b两个数组,空间消耗也是线性的。因此,整个算法的额外空间复杂度为O(n)。
C++ 代码
#include <bits/stdc++.h>
using namespace std;
const int N = 200010;
int T, n, a[N], pre[N], suf[N];
void solve() {
pre[2] = 1;
suf[n - 1] = 1;
for(int i = 3; i <= n; i++) {
int p = __gcd(a[i - 2], a[i - 1]), c = __gcd(a[i - 1], a[i]);
pre[i] = pre[i - 1] + (p <= c? 1: 0);
}
for(int i = n - 2; i >= 1; i--) {
int s = __gcd(a[i + 1], a[i + 2]), c = __gcd(a[i + 1], a[i]);
suf[i] = suf[i + 1] + (c <= s? 1: 0);
}
// 删除1或n
bool ok = false;
// 删除[3,n-2]
for(int i = 3; i <= n - 2 && !ok; i++) {
// 删除a[i]
int l = __gcd(a[i - 2], a[i - 1]);
int m = __gcd(a[i - 1], a[i + 1]);
int r = __gcd(a[i + 1], a[i + 2]);
if(pre[i - 1] == i - 2 && suf[i + 1] == n - i - 1 && (l <= m && m <= r)) {
ok = true;
}
}
// 删除前两个或后两个元素
if(!ok) {
unordered_set<int> pos{1, 2, n - 1, n};
for(int index: pos) {
// 删除index
vector<int> arr;
for(int i = 1; i <= n; i++) {
if(i == index) continue;
arr.push_back(a[i]);
}
vector<int> b;
for(int i = 1; i < arr.size(); i++) {
b.push_back(__gcd(arr[i - 1], arr[i]));
}
ok = is_sorted(b.begin(), b.end());
if(ok) break;
}
}
puts(ok? "YES": "NO");
}
int main() {
scanf("%d", &T);
while(T--) {
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
solve();
}
return 0;
}