CF1175F The Number of Subpermutations
Solution
考虑在什么条件下子区间 \([l,r]\) 是 \(1 到 r - l + 1\) 的排列,
-
\([l,r]\) 中没有重复的数字
-
\([l,r]\) 中最大值为 \(r - l + 1\)
于是可以分治,每次考虑在 \([l,r]\) 中且经过 \(mid\) 的子区间,其中 \(mid\) 是 \([l,r]\) 中最大值的位置,那么这些区间的最大值就是 \(a_{mid}\) 了,接下来考虑第一个条件。
可以记录下每个数上一次出现的位置 \(pre\),然后只需要判断区间中最大的 \(pre\) 是否大于区间左端点。
其实可以判断 \(pre\) 的前缀最大值,因为在左端点前的 \(pre\) 一定是小与左端点的。
注意在枚举区间的时候要判断 \(mid\) 左右哪边小,枚举小的那边作为区间的一端,因为区间长度一定是 \(a_{mid}\),所以另一端就确定了,不然可能会被卡成 \(O(n^2)\)。
Code
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
const int N = 3e5 + 5;
int n, a[N];
int pre[N], lst[N];
int st[N][20], f[N];
int ans;
void ST()
{
for(int i = 1; i <= n; i++)
st[i][0] = i;
int lg = log2(n);
for(int j = 1; j <= lg; j++)
for(int i = 1; i + (1 << j) - 1 <= n; i++)
st[i][j] = a[st[i][j - 1]] >= a[st[i + (1 << (j - 1))][j - 1]] ? st[i][j - 1] : st[i + (1 << (j - 1))][j - 1];
}
int qry(int l, int r)
{
int lg = log2(r - l + 1);
return a[st[l][lg]] >= a[st[r - (1 << lg) + 1][lg]] ? st[l][lg] : st[r - (1 << lg) + 1][lg];
}
void solve(int l, int r)
{
if(l > r) return;
if(l == r)
{
if(a[l] == 1) ans++;
return;
}
int mid = qry(l, r);
if(mid - l + 1 <= r - mid)
{
for(int i = l ; i <= mid; i++)
{
int j = i + a[mid] - 1;
if(j >= mid && j <= r && f[j] < i) ans++;
}
}
else
{
for(int j = mid; j <= r; j++)
{
int i = j - a[mid] + 1;
if(i <= mid && i >= l && f[j] < i) ans++;
}
}
solve(l, mid - 1);
solve(mid + 1, r);
return;
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i++)
scanf("%d", &a[i]), pre[i] = lst[a[i]], lst[a[i]] = i;
ST();
for(int i = 1; i <= n; i++)
f[i] = max(f[i - 1], pre[i]);
solve(1, n);
printf("%d\n", ans);
return 0;
}
$$A\ drop\ of\ tear\ blurs\ memories\ of\ the\ past.$$