C1. Good Subarrays (Easy Version)
C1. Good Subarrays (Easy Version)
This is the easy version of this problem. In this version, we do not have queries. Note that we have multiple test cases in this version. You can make hacks only if both versions of the problem are solved.
An array of length is good if for all the -th element is greater than or equal to . In other words, is good if and only if for all ().
You are given an array consisting of positive integers. Find the number of pairs of indices , where , such that the array is good.
Input
Each test contains multiple test cases. The first line contains the number of test cases (). Description of the test cases follows.
The first line of each test case contains an integer (), the length of the array .
The second line of each test case contains space-separated integers , representing the array .
It is guaranteed that the sum of over all test cases does not exceed .
Output
For each test case, print the number of suitable pairs of indices.
Example
input
3 3 1 2 3 3 1 1 1 4 2 1 4 3
output
6 3 7
Note
In the first test case, all subarrays of are good, so all pairs are suitable.
In the second test case, the pairs , , and are suitable. For example, when , the array is not good because .
解题思路
比赛的时候是写线段树过的。先说一下思路,首先对于每一个下标,看看它最左可以到达哪里,这个下标位置就是。然后在区间中,可能会存在某些位置能到达最左边的下标位置会大于,因此要在区间中找到最大的,那么就是以下标为结尾能够向左取到满足要求的最长的子数组。
因为涉及到区间求最值问题,因此可以用线段树来维护各个区间的最大值。
AC代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 typedef long long LL; 5 6 const int N = 2e5 + 10; 7 8 int a[N]; 9 struct Node { 10 int l, r, maxv; 11 }tr[N * 4]; 12 13 void build(int u, int l, int r) { 14 if (l == r) { 15 tr[u] = {l, r, max(1, l - a[l] + 1)}; 16 } 17 else { 18 int mid = l + r >> 1; 19 build(u << 1, l, mid); 20 build(u << 1 | 1, mid + 1, r); 21 tr[u] = {l, r, max(tr[u << 1].maxv, tr[u << 1 | 1].maxv)}; 22 } 23 } 24 25 int query(int u, int l, int r) { 26 if (tr[u].l >= l && tr[u].r <= r) return tr[u].maxv; 27 int mid = tr[u].l + tr[u].r >> 1, ret = 0; 28 if (l <= mid) ret = query(u << 1, l, r); 29 if (r >= mid + 1) ret = max(ret, query(u << 1 | 1, l, r)); 30 return ret; 31 } 32 33 void solve() { 34 int n; 35 scanf("%d", &n); 36 for (int i = 1; i <= n; i++) { 37 scanf("%d", a + i); 38 } 39 build(1, 1, n); 40 LL ret = 0; 41 for (int i = 1; i <= n; i++) { 42 int t = query(1, max(1, i - a[i] + 1), i); // 求区间[p[j], i]中的最大的p[j] 43 ret += i - t + 1; // 最大长度子数组区间就是[p[j], i] 44 } 45 printf("%lld\n", ret); 46 } 47 48 int main() { 49 int t; 50 scanf("%d", &t); 51 while (t--) { 52 solve(); 53 } 54 55 return 0; 56 }
实际上并没有这么复杂,可以用双指针来实现。
假设以为结尾满足要求的子数组最靠左能到达的位置是,这时往后移动到达,对应的最靠左能够到达的位置是,同时有。这意味着在区间中,所有位置均满足,即对于来说最靠左能到达的位置可以是,这就矛盾了。因此当往右移动时,指针也是往右移动,因此两个指针具有单调性,可以用双指针。
AC代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 typedef long long LL; 5 6 const int N = 2e5 + 10; 7 8 int a[N]; 9 10 void solve() { 11 int n; 12 scanf("%d", &n); 13 for (int i = 1; i <= n; i++) { 14 scanf("%d", a + i); 15 } 16 LL ret = 0; 17 for (int i = 1, j = 1; i <= n; i++) { 18 while (a[i] < i - j + 1) { 19 j++; 20 } 21 ret += i - j + 1; 22 } 23 printf("%lld\n", ret); 24 } 25 26 int main() { 27 int t; 28 scanf("%d", &t); 29 while (t--) { 30 solve(); 31 } 32 33 return 0; 34 }
参考资料
Codeforces Round #825 (Div. 2) Editorial:https://codeforces.com/contest/1736
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/16779588.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效