递增三元组(第九届蓝桥杯)

文章介绍了如何优化递增三元组问题的解决方案,通过二分查找减少a[]和c[]的枚举时间,以及利用双指针和前缀和的方法计算符合条件的组合。
摘要由CSDN通过智能技术生成

题目

在这里插入图片描述

原题链接

递增三元组

思路分析

由时间复杂度可知需要至少优化到 O ( n l o g n ) O(nlogn) O(nlogn)才行
而纯暴力枚举三个数组的话: O ( n 3 ) O(n^3) O(n3)
可以考虑将b[]作为标志,枚举a[]中小于b[i]的和c[]中大于b[i]
既然是查找
这种类型的题目可以想到用二分或者双指针来降低枚举的时间复杂度

二分做法1
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 100010;
int a[N], b[N], c[N];
int n;
bool check_a(int mid, int x) {
    if(a[mid] < x) return true;
    else return false;
}
bool check_c(int mid, int x) {
    if(c[mid] > x) return true;
    else return false;
}
int main() {
    cin >> n;
    for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
    for(int i = 1; i <= n; i++) scanf("%d", &b[i]);    
    for(int i = 1; i <= n; i++) scanf("%d", &c[i]);
    sort(a + 1, a + 1 + n);
    sort(b + 1, b + 1 + n);
    sort(c + 1, c + 1 + n);
    LL res = 0;
    for(int i = 1; i <= n; i++) {   //以b[]为参照物
        int st = 0, ed = 0;
        int la = 1, ra = n, lc = 1, rc  = n;
        while(la < ra) {
            int mid = la + ra + 1 >> 1;
            if(check_a(mid, b[i]))    la = mid;
            else ra = mid - 1;
        }
        if(a[ra] < b[i])     st = ra;
        while(lc < rc) {
            int mid = lc + rc >> 1;
            if(check_c(mid, b[i]))  rc = mid;
            else lc = mid + 1;
        }
        if(c[rc] > b[i])    ed = n - rc + 1;
        res += (LL)st * ed;
    }
    printf("%lld", res);
    return 0;
}
二分做法2

上方的只需要变动这块就行,直接调用库函数

    for(int i = 1; i <= n; i++) {   //以b[]为参照物
        int st = 0, ed = 0;
        int la = 1, ra = n, lc = 1, rc  = n;
        // while(la < ra) {
        //     int mid = la + ra + 1 >> 1;
        //     if(check_a(mid, b[i]))    la = mid;
        //     else ra = mid - 1;
        // }
        // if(a[ra] < b[i])     st = ra;
    //要想找第一个小于的,那么可用第一个大于等于的-1
        st = lower_bound(a + 1, a + 1 + n, b[i]) - a - 1;
        // while(lc < rc) {
        //     int mid = lc + rc >> 1;
        //     if(check_c(mid, b[i]))  rc = mid;
        //     else lc = mid + 1;
        // }
        // if(c[rc] > b[i])    ed = n - rc + 1;
    //找第一个大于的可直接用upper
        ed = n - (upper_bound(c + 1, c + n + 1, b[i]) - c) + 1;
        res += (LL)st * ed;
    }
双指针做法
//双指针,只需修改上方为下面这样即可
int l = 1, r = 1;
for(int i = 1; i <= n; ++i) {
    while(a<=n && a[l] < b[i]) l++;
    while(c<=n && c[r] <= b[i]) r++;

    ans += (LL)(l-1)*(n-r+1);
}
前缀和解法

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 100010;
int n;
int a[N],b[N],c[N];
int as[N];  //as[N]表示在A[]中有多少个数小于b[i]
int cs[N];  //cs[N]表示在C[]中有多少个数小于b[i]
int cnt[N],s[N]; //s[i]表示1~i之间的数的数量

int main()
{
    cin >> n;
    long long res = 0;
    //a,b,c++的目的是为了范围从1开始,以便后续使用前缀和
    for(int i = 0;i < n;i++)    scanf("%d",&a[i]),a[i]++;
    for(int i = 0;i < n;i++)    scanf("%d",&b[i]),b[i]++;
    for(int i = 0;i < n;i++)    scanf("%d",&c[i]),c[i]++;
   
   //求as[i]
   for(int i = 0;i < n;i++) cnt[a[i]]++;    //某个数的数量
   for(int i = 1;i < N;i++) s[i] = s[i-1] + cnt[i]; //前缀和
   for(int i = 0;i < n;i++) as[i] = s[b[i] - 1];    //1到b[i]之间的数的数量
   
   //求cs[i]
    memset(cnt,0,sizeof cnt);   //将cnt重置为0
    memset(s,0,sizeof s);       //将s重置为0
    for(int i = 0;i < n;i++)    cnt[c[i]]++;
    for(int i = 1;i < N;i++)    s[i] = s[i-1] + cnt[i];
    for(int i = 0;i < n;i++)    cs[i] = s[N-1] - s[b[i]];
       
   //枚举每个b[i]
    for(int i = 0;i < n;i++)    res += (long long)as[i] * cs[i];
 
    cout << res << endl;
    
    return 0;
}
  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值