AcWing 1236:递增三元组

题目入口

题目跳转

题目描述

给定三个整数数组

A=[A1,A2,…AN], B=[B1,B2,…BN], C=[C1,C2,…CN],

请你统计有多少个三元组 (i,j,k) 满足:

1.1≤i,j,k≤N
2.Ai

输入格式

第一行包含一个整数 N。

第二行包含 N 个整数 A1,A2,…AN。

第三行包含 N 个整数 B1,B2,…BN。

第四行包含 N 个整数 C1,C2,…CN。

输出格式

一个整数表示答案。

数据范围

1≤N≤1e5
0≤Ai,Bi,Ci≤1e5

输入样例

3
1 1 1
2 2 2
3 3 3

输出样例

27

思路讲解

看过题之后,我们首先联想到的解题方法就是三重循环,将符合要求的进行计数处理,最后输出答案,但是这个做法的时间复杂度是O(n^3),大概就是1e15,超过了时限,所以我们应该对其进行优化,我们可以看一下n的数据范围是1e5,所以这道题的做法时间复杂度应该是O(nlogn)或者是O(n),所以我们只能去枚举A,B,C这三个其中的一个,我们选择枚举B的话,可以对A和C产生一个直接的限制,所以我们选择枚举B,我们枚举每一个B中的元素,求出符合要求的A和C的数量,记为cnta,cntc,然后cnta*cntc即为这一次循环得到符合的三元组的数量,计算出每一次循环的符合要求的三元组的数量,然后将其加和即为答案,我们求符合要求的A和C的数量可以使用排序加二分和前缀和这两种任意一种来求解。

代码

排序加二分
#include
#include

using namespace std;

const int N = 1e5+10;
int n;
int A[N],B[N],C[N];
//记录符合要求的三元组的数量
long long ans;

/*
算法思路:
枚举B然后看A和C中哪些满足条件即可
本次使用的是排序+二分
*/

int main()
{
    cin >> n;
    for(int i = 1;i <= n;i++)
    {
        cin >> A[i];
    }   
    for(int i = 1;i <= n;i++)
    {
        cin >> B[i];
    }  
    for(int i = 1;i <= n;i++)
    {
        cin >> C[i];
    }  
    sort(A+1,A+n+1);
    sort(C+1,C+n+1);
    for(int i = 1;i <= n;i++)
    {
        int l = 1;
        int r = n;
        while(l < r)
        {
            int mid = l + r + 1 >> 1;
            if(A[mid] >= B[i])r = mid - 1;
            else l = mid;
        }
        long long tempA;
        if(A[l] >= B[i])tempA = 0;
        else tempA = l;
        l = 1;
        r = n;
        while(l < r)
        {
            int mid = l + r >> 1;
            if(C[mid] > B[i])r = mid;
            else l = mid + 1;
        }
        long long tempC;
        if(C[l] <= B[i])tempC = 0;
        else tempC = n - l + 1;
        ans += tempA * tempC;
    }
    cout << ans << endl;
    return 0;
}
前缀和
#include
#include
#include

using namespace std;

typedef long long LL;

const int N = 1e5+10;
int n;
int A[N],B[N],C[N];
//sa[i]记录A中1到i中每个数出现的次数的总和
//sc[i]记录A中1到i中每个数出现的次数的总和
int sa[N],sc[N];


int main()
{
    cin >> n;
    //由于A[i],B[i],C[i]都有可能取到0,所以为了避免前缀和的边界问题,将其都加1
    for(int i = 1;i <= n;i++)scanf("%d",&A[i]),A[i]++;
    for(int i = 1;i <= n;i++)scanf("%d",&B[i]),B[i]++;
    for(int i = 1;i <= n;i++)scanf("%d",&C[i]),C[i]++;
    
    for(int i = 1;i <= n;i++)sa[A[i]]++;
    //计算前缀和
    for(int i = 1;i < N;i++)sa[i] += sa[i-1];

    for(int i = 1;i <= n;i++)sc[C[i]]++;
    //计算前缀和
    for(int i = 1;i < N;i++)sc[i] += sc[i-1];


    LL res = 0;
    for(int i = 1;i <= n;i++)
    {
        int cnta = sa[B[i]-1];
        int cntc = sc[N-1] - sc[B[i]];

        res += (LL)cnta * cntc;

    }

    cout << res << endl;

    return 0;
}

你可能感兴趣的:(Acwing,算法,c++,算法)