蓝桥杯C++ AB组辅导课 第四讲 枚举、模拟与排序 Acwing

阅读 97

2022-02-08

例题

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;
}

AcWing 1245. 特别数的和

AcWing 1204. 错误票据

AcWing 466. 回文日期

AcWing 787. 归并排序

习题

AcWing 1219. 移动距离

AcWing 1229. 日期问题

AcWing 1231. 航班时间

AcWing 1241. 外卖店优先级

AcWing 788. 逆序对的数量

精彩评论(0)

0 0 举报