0
点赞
收藏
分享

微信扫一扫

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

流计算Alink 2022-02-08 阅读 97

例题

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 条评论