#P1750. 第2题-小红删数组
-
1000ms
Tried: 125
Accepted: 28
Difficulty: 7
所属公司 :
阿里
时间 :2024年3月27日-阿里淘天
-
算法标签>双指针
第2题-小红删数组
思路1:枚举+二分
考虑任选一个区间 [l,r] 删除,剩余的左部分是 [1,l−1] ,右部分是 [r+1,n]
需要保证 a[1,l−1] 是单调递增,a[r+1,n] 也是单调递增的,且满足 a[l−1]≤a[r+1]
枚举区间 [l,r] 的左端点 left,,来二分出区间的右端点 right,check 函数是 a[left−1]≤a[right+1]
我们需要保证 a[1]≤a[2]≤⋯≤a[left−1] ,所以枚举左端点 left ,直到 a[left]<a[left−1] 停止。
之后需要确定 right ,满足 $a[left-1]\leq a[right+1]\leq a[right+2]\leq \cdots\leq a[n]$ 。
这部分可以用二分答案来解决,而我们需要保证二分的区间是递增的,所以需要先预处理出最长的单调递增的后缀。
时间复杂度:O(nlogn)
代码
n = int(input())
a = list(map(int, input().split()))
a = [0] + a + [2 * 10 ** 9]
# 预处理后缀
# 通过枚举 left ,确定 right
# a[left - 1] <= a[right + 1]
ok = [False for _ in range(len(a))]
ok[n + 1] = True
min_r = n
for i in range(n, -1, -1):
if ok[i + 1] and a[i] <= a[i + 1]:
ok[i] = True
min_r = i
else:
break
# 枚举区间左端点 left
# 二分区间右端点 right
ans = 0
for left in range(1, n + 1):
l, r = max(left, min_r - 1), n
while l < r:
right = (l + r) >> 1
if a[right + 1] >= a[left - 1]:
r = right
else:
l = right + 1
ans += n - l + 1
if a[left] < a[left - 1]:
break
print(ans)
思路2:双指针
从上面的思路来看,我们枚举左端点,值是单调递增的,那么对于越大的左端点来说,其右端点位置也必然是单调递增的。
所以可以预处理出后缀单调递增的起始位置。从这个位置 right 开始,我们枚举左端点 left ,不断移动右端点 right 使得 a[right+1]≥a[left−1] 即可。
时间复杂度:O(n)
python
n = int(input())
a = list(map(int, input().split()))
a = [0] + a + [2 * 10 ** 9]
min_r = n
for i in range(n, 0, -1):
if a[i] <= a[i + 1]:
min_r = i
else:
break
ans = 0
right = min_r - 1
for left in range(1, n + 1):
right = max(right, left)
while right <= n and a[right + 1] < a[left - 1]:
right += 1
ans += n - right + 1
if a[left] < a[left - 1]:
break
print(ans)
C++
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int t;
const int mod = 1e9 + 7;
ll ans2 = 0;
bool check(vector<int> &c) {
for (int i = 0; i < c.size(); i++) {
//cout << c[i] << " ";
}
for (int i = 1; i < c.size(); i++) {
if (c[i] < c[i - 1]) {
return false;
}
}
return true;
}
void bf(vector<int> &a) {
ans2 = 0;
for (int l = 0; l < a.size(); l++) {
for (int r = l; r < a.size(); r++) {
vector<int> c;
for (int i = 0; i < l; i++) {
c.push_back(a[i]);
}
for (int i = r + 1; i < a.size(); i++) {
c.push_back(a[i]);
}
if (check(c)) {
//cout << l << " " << r << endl;
ans2++;
}
}
}
}
void solve() {
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
// a[i] = rand() % 5 + 1;
}
vector<int> pre;
vector<int> preId;
vector<int> nxt;
vector<int> nxtId;
pre.push_back(a[0]);
preId.push_back(0);
for (int i = 1; i < n; i++) {
if (a[i] >= a[i - 1]) {
pre.push_back(a[i]);
preId.push_back(i);
} else {
break;
}
}
nxt.push_back(a[n - 1]);
nxtId.push_back(n - 1);
for (int i = n - 2; i >= 0; i--) {
if (a[i] <= a[i + 1]) {
nxt.push_back(a[i]);
nxtId.push_back(i);
} else {
break;
}
}
reverse(nxt.begin(), nxt.end());
reverse(nxtId.begin(), nxtId.end());
ll ans = 1ll * nxt.size() * (nxt.size() + 1) / 2;
//bf(a);
if (preId[0] != nxtId[0]) {
ans = (int) pre.size() + (int) nxt.size() + 1;
for (int i: pre) {
int id = lower_bound(nxt.begin(), nxt.end(), i) - nxt.begin();
ans += (nxt.size() - id);
}
}
cout << ans << endl;
/*if (ans2 != ans) {
cout << "WA" << ans2 << " " << ans << endl;
for (int i: a) {
cout << i << " ";
}
cout << endl;
exit(0);
} else {
cout << "AC" << endl;
}*/
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
srand(time(NULL));
//cin >> t;
t = 1;
while (t--) {
solve();
}
}
java
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int t;
const int mod = 1e9 + 7;
ll ans2 = 0;
bool check(vector<int> &c) {
for (int i = 0; i < c.size(); i++) {
//cout << c[i] << " ";
}
for (int i = 1; i < c.size(); i++) {
if (c[i] < c[i - 1]) {
return false;
}
}
return true;
}
void bf(vector<int> &a) {
ans2 = 0;
for (int l = 0; l < a.size(); l++) {
for (int r = l; r < a.size(); r++) {
vector<int> c;
for (int i = 0; i < l; i++) {
c.push_back(a[i]);
}
for (int i = r + 1; i < a.size(); i++) {
c.push_back(a[i]);
}
if (check(c)) {
//cout << l << " " << r << endl;
ans2++;
}
}
}
}
void solve() {
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
// a[i] = rand() % 5 + 1;
}
vector<int> pre;
vector<int> preId;
vector<int> nxt;
vector<int> nxtId;
pre.push_back(a[0]);
preId.push_back(0);
for (int i = 1; i < n; i++) {
if (a[i] >= a[i - 1]) {
pre.push_back(a[i]);
preId.push_back(i);
} else {
break;
}
}
nxt.push_back(a[n - 1]);
nxtId.push_back(n - 1);
for (int i = n - 2; i >= 0; i--) {
if (a[i] <= a[i + 1]) {
nxt.push_back(a[i]);
nxtId.push_back(i);
} else {
break;
}
}
reverse(nxt.begin(), nxt.end());
reverse(nxtId.begin(), nxtId.end());
ll ans = 1ll * nxt.size() * (nxt.size() + 1) / 2;
//bf(a);
if (preId[0] != nxtId[0]) {
ans = (int) pre.size() + (int) nxt.size() + 1;
for (int i: pre) {
int id = lower_bound(nxt.begin(), nxt.end(), i) - nxt.begin();
ans += (nxt.size() - id);
}
}
cout << ans << endl;
/*if (ans2 != ans) {
cout << "WA" << ans2 << " " << ans << endl;
for (int i: a) {
cout << i << " ";
}
cout << endl;
exit(0);
} else {
cout << "AC" << endl;
}*/
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
srand(time(NULL));
//cin >> t;
t = 1;
while (t--) {
solve();
}
}
题目描述
小红有一个长度为n的数组a,他想要使得数组a有序(单调不降),他必须选择一段区间[l,r](1≤l,r≤n),将数组的这一段删除,其他的部分(如果存在的话)就按顺序拼在一起。 现在他想知道有多少种不同的选择区间的方案。
输入描述
第一行一个正整数n(1≤n≤2×105),表示数组的长度。
第二行n个正整数ai(1≤a¡≤109),表示数组a。
输出描述
输出一行一个正整数表示答案。
样例
输入
3
1 2 3
输出
6
说明
可以选择:
[1, 1], [2, 2], [3, 3], [1, 2], [2, 3], [1, 3]
这六个区间