题目描述:
给出一个长度为 $ n $ 的排列,求可以拼出多少个,最多分成 2
段的值域上连续区间
数据范围:
$ 1\le T \le 10^4 , 1\le n \le 5000, \sum n \le 10^4 $
题解:
需要维护每个区间联通块的数量,联通块的数目为 $ 1 $ 或 $ 2 $ 的时候答案加 $ 1 $ 。联通块的数目大于 $ 2 $ 时,对答案没有贡献。
首先,需要转换一下, $ b[a[i]] = i $ ,这样的话,直接枚举 $ b $ 中的区间就是枚举连续区间,看每段连续区间分成几段。不能直接枚举 $ a $ 的区间,因为 $ a $ 中可能会有 $ 1,2,4,5,\cdots $ 的序列,得到 $ 1,2,4,5 $ 的区间,该区间不连续,虽然有两段,但是并不能计入答案。
维护联通块的数量,可以使用递推的方法
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| using namespace std; using namespace FAST_IO; const ll mod = 1e9 + 7; const int INF = 0x3f3f3f3f; const ll INF_LL = 0x3f3f3f3f3f3f3f3f; const double eps = 1e-5; const int maxn = 1e4 + 10; const int maxm = 1e5 + 10; int t, n, m, k; int a[maxn]; int b[maxn]; bool flag[maxn]; int main() {
#ifndef ONLINE_JUDGE freopen("in.txt", "r", stdin); #endif int tcase; read(tcase); while (tcase--) { read(n); for (int i = 1; i <= n; i++) { read(a[i]); b[a[i]] = i; } ll ans = 0; for (int i = 1; i <= n; i++) { int cnt = 0; memset(flag, 0, sizeof(flag)); for (int j = i; j <= n; j++) { if (!flag[b[j] - 1] && !flag[b[j] + 1]) ++cnt; else if (flag[b[j] - 1] && flag[b[j] + 1]) --cnt; if (cnt < 3) ++ans; flag[b[j]] = 1; } } cout << (n == 1 ? 0 : ans) << endl; } return 0; }
|