Codeforces 1167E 尺取法
题意:给你一个长度为n的数组,以及数组中的数的取值范围1 - m,问有多少个区间[l, r],使得删除了数组中数值为[l, r]的数之后,数组是非递减的。
思路:我们记录一下每一个数出现的最左端和最右端的位置,这样形成的若干个区间,如果数组是非递减的,那么一定是下列这种情况:范围1 - n中有若干个不相交的区间,并且区间所代表的数从左到右是递增的。所以我们想到如下算法:我们先预处理出从最右端一直向左可以找出多少个数所代表的区间不相交,然后枚举从最左端添加区间,先保证左右没有相交,然后计算答案,如果左边出现了相交的情况就break,因为后面肯定不满足答案了。
代码:
#include <bits/stdc++.h> #define LL long long #define INF 0x3f3f3f3f #define db double #define pii pair<int, int> using namespace std; const int maxn = 1000010; int l[maxn], r[maxn]; int b[maxn]; LL cnt[maxn]; int main() { LL n, m; scanf("%lld%lld", &n ,&m); memset(l, 0x3f, sizeof(l)); for (int i = 1; i <= n; i++) { scanf("%d", &b[i]); l[b[i]] = min(l[b[i]], i); r[b[i]] = max(r[b[i]], i); cnt[b[i]]++; } LL L = n + 1, R = -1, pos = -1; LL tot = 0, ans = 0; for (int i = m; i >= 1; i--) { if(cnt[i] == 0) { l[i] = l[i + 1]; continue; } if(r[i] < L) { L = l[i]; } else { tot = m - i + 1; pos = i + 1; break; } } if(pos == -1) { ans = (m * (m + 1)) / 2; printf("%lld\n", ans); return 0; } for (int i = 0; i <= m; i++) { if(cnt[i] == 0) { ans += tot; continue; } if(l[i] < R) { break; } else { if(cnt[i] == 0) r[i] = r[i - 1]; R = r[i]; while(L <= R && pos <= m) { if(cnt[pos] == 0) { tot--; pos++; L = l[pos]; } else { pos++; tot--; L = l[pos]; } } ans += tot; } } printf("%lld\n", ans); return 0; }