题意:给定一个1-n的排列,选取两个区间,所选取的两个区间的下标不能重合(比如选[1,3]和[2,4]),把这两个区间合并后得到的集合必须是连续的。问有多少种选法。
思路:
N<5000考虑N^2做法,枚举合并后的集合C,判断其是否能被得到(也就是选最多两个区间能覆盖这个集合)。记录下数字对应的下标,枚举集合[i,j]是否能被选到,用一个数组记录下集合中对应元素的下标位置在哪,边枚举边维护覆盖集合C需要的区间数量。具体细节见注释。
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
const int N = 1e6 + 1;
const int mod = 998244353;
int a[N];
int b[N];
void solve()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++)
{
int x;
cin >> x;
a[x] = i; // 存下标
}
if (n == 1)
{
cout << 0 << endl;
return;
}
int res = 0;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
b[j] = 0;
}
int cnt = 0; // cnt是覆盖集合C=[i,j]这个区间所需要选择的区间数量
for (int j = i; j <= n; j++) // 判断C == i到j 这个集合能否被两个区间以下覆盖
{
int idx = a[j];
if (b[idx - 1] == 0 && b[idx + 1] == 0) // 如果这个下标的左右都没被标记,说明这是一个新区间。
cnt++;
if (b[idx - 1] == 1 && b[idx + 1] == 1) // 左右都被标记过,说明能合并左右两个区间
cnt--;
// 其他情况也就是左边和右边有一个被标记过,说明可以这个数字可以合并到这个区间,cnt不变
b[idx] = 1;
if (cnt <= 2) // 如果覆盖集合C=[i,j]这个区间所需要选择的区间数量小于等2,说明这个集合合法
res++;
}
}
for (int i = 1; i <= n; i++)
{
a[i] = 0;
b[i] = 0;
}
cout << res << endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t = 1;
cin >> t;
while (t--)
solve();
}