例题
AcWing 1210. 连号区间数
小明这些天一直在思考这样一个奇怪而有趣的问题:
在 1∼N 的某个排列中有多少个连号区间呢?
这里所说的连号区间的定义是:
如果区间 [L,R] 里的所有元素(即此排列的第 L 个到第 R 个元素)递增排序后能得到一个长度为 R−L+1 的“连续”数列,则称这个区间连号区间。
当 N 很小的时候,小明可以很快地算出答案,但是当 N 变大的时候,问题就不是那么简单了,现在小明需要你的帮助。
输入格式
第一行是一个正整数 N,表示排列的规模。
第二行是 N 个不同的数字 Pi,表示这 N 个数字的某一排列。
输出格式
输出一个整数,表示不同连号区间的数目。
数据范围
1≤N≤10000,
1≤Pi≤N
输入样例1:
4
3 2 4 1
输出样例1:
7
输入样例2:
5
3 4 2 5 1
输出样例2:
9
样例解释
第一个用例中,有 7 个连号区间分别是:[1,1],[1,2],[1,3],[1,4],[2,2],[3,3],[4,4]
第二个用例中,有 9 个连号区间分别是:[1,1],[1,2],[1,3],[1,4],[1,5],[2,2],[3,3],[4,4],[5,5]
思路 :
- 1e4,可以枚举所有区间
- 注意到输入数据保证每个元素值都不同,因此,连续 等价于 最大值减最小值等于区间长度-1
- 显然不可能通过每次排序来得到每个区间的最大最小值,因为我们顺序枚举出了所有区间,因此可以采用动态更新的方法
#include <iostream>
using namespace std;
const int N = 1e4 + 10;
int n, a[N];
int main()
{
cin >> n;
for (int i = 1; i <= n && cin >> a[i]; i ++ );
int res = 0;
for (int i = 1; i <= n; i ++ )
{
int mx = 0, mi = 1e5;
for (int j = i; j <= n; j ++ )
{
mx = max(mx, a[j]);
mi = min(mi, a[j]);
if (j - i + 1 == mx - mi + 1) res ++ ;
}
}
cout << res;
}
AcWing 1236. 递增三元组
给定三个整数数组
A=[A1,A2,…AN],
B=[B1,B2,…BN],
C=[C1,C2,…CN],
请你统计有多少个三元组 (i,j,k) 满足:
1≤i,j,k≤N
Ai<Bj<Ck
输入格式
第一行包含一个整数 N。
第二行包含 N 个整数 A1,A2,…AN。
第三行包含 N 个整数 B1,B2,…BN。
第四行包含 N 个整数 C1,C2,…CN。
输出格式
一个整数表示答案。
数据范围
1≤N≤105,
0≤Ai,Bi,Ci≤105
输入样例:
3
1 1 1
2 2 2
3 3 3
输出样例:
27
思路(二分):
- 二分前记得先sort
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int n;
int a[3][N];
int main()
{
cin >> n;
for (int i = 0; i < 3; i ++ )
for (int j = 1; j <= n; j ++ )
cin >> a[i][j];
for (int i = 0; i < 3; i ++ ) sort(a[i] + 1, a[i] + n + 1); // 二分
ll res = 0;
for (int i = 1; i <= n; i ++ )
{
int cnta = lower_bound(a[0] + 1, a[0] + n + 1, a[1][i]) - a[0] - 1;
int cntc = n - (upper_bound(a[2] + 1, a[2] + n + 1, a[1][i]) - a[2]) + 1;
res += (ll)cnta * cntc;
}
cout << res;
}
思路(双指针):
- 对查找进一步优化,找排好序后a数组中小于key的个数可以用双指针算法,c数组同理
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int n;
int a[3][N];
int main()
{
cin >> n;
for (int i = 0; i < 3; i ++ )
for (int j = 1; j <= n; j ++ )
cin >> a[i][j];
for (int i = 0; i < 3; i ++ ) sort(a[i] + 1, a[i] + n + 1); // 二分
ll res = 0;
int pa = 1, pc = 1;
for (int i = 1; i <= n; i ++ )
{
int key = a[1][i];
while (pa <= n && a[0][pa] < key) pa ++ ; // >=
while (pc <= n && a[2][pc] <= key) pc ++ ; // >
res += (ll)(pa - 1) * (n - pc + 1);
}
cout << res;
}
思路 (前缀和):
- 每次都是找出a数组中小于b[i]的个数,因此可以预处理出这个个数
- 首先记录a数组中每个值对应数量,由于元素值范围只有 1 0 5 10^5 105,因此直接用数组即可
- 注意到这题元素值可能为0,因此整体偏移增加1,不影响相对大小
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int n;
int cnta[N], b[N], cntc[N];
int main()
{
cin >> n;
for (int i = 1, x; i <= n && cin >> x; i ++ ) cnta[ ++ x] ++ ;
for (int i = 1; i <= n && cin >> b[i]; i ++ ) b[i] ++ ;
for (int i = 1, x; i <= n && cin >> x; i ++ ) cntc[ ++ x] ++ ;
// cnta[0] = cnta[0];
for (int i = 1; i < N; i ++ ) cnta[i] += cnta[i - 1];
// cntc[0] = cntc[0];
for (int i = 1; i < N; i ++ ) cntc[i] += cntc[i - 1];
ll res = 0;
for (int i = 1; i <= n; i ++ )
{
int key = b[i];
res += (ll)cnta[key - 1] * (cntc[N - 1] - cntc[key]);
}
cout << res;
}