[蓝桥杯][2018年第九届真题]递增三元组

二分做法

考虑到(A_i < B_j < C_k),如果以(A_i)为基准进行二分,时间复杂度为:(O(n^2log^2n))

若以(B_j)为基准,则只需在(A)数组中查找最后一个小于(B_j)的位置,以及(C)数组中查找第一个大于(B_j)的位置,时间复杂度为:(O(nlogn))

const int N=1e5+10;
int a[N],b[N],c[N];
int n;

int main()
{
    cin>>n;

    for(int i=0;i<n;i++) cin>>a[i];
    sort(a,a+n);
    for(int i=0;i<n;i++) cin>>b[i];
    sort(b,b+n);
    for(int i=0;i<n;i++) cin>>c[i];
    sort(c,c+n);

    LL res=0;
    for(int i=0;i<n;i++)
    {
        int ra=lower_bound(a,a+n,b[i])-a;
        int lc=upper_bound(c,c+n,b[i])-c;
        res+=(LL)ra*(n-lc);
    }
    cout<<res<<endl;
    //system("pause");
    return 0;
}

双指针做法

由于存在单调性,可用双指针替代二分。

const int N=1e5+10;
int a[N],b[N],c[N];
int n;

int main()
{
    cin>>n;

    for(int i=0;i<n;i++) cin>>a[i];
    sort(a,a+n);
    for(int i=0;i<n;i++) cin>>b[i];
    sort(b,b+n);
    for(int i=0;i<n;i++) cin>>c[i];
    sort(c,c+n);

    LL res=0;
    int ra=0,lc=0;
    for(int i=0;i<n;i++)
    {
        while(ra<n && a[ra] < b[i]) ra++;
        while(lc<n && c[lc] <= b[i]) lc++;
        res+=(LL)ra*(n-lc);
    }
    cout<<res<<endl;
    //system("pause");
    return 0;
}

前缀和做法

开两个桶分别记录数组(a)(c)中各数字出现的次数,对桶数组求前缀和,之后枚举(b)数组中的每个数累加答案。

为方便前缀和处理,将(a)(b)(c)读入的数字均做加一处理。

const int N=1e5+10;
int a[N],b[N],c[N];
int cnta[N],cntc[N];
int suma[N],sumc[N];
int n;

int main()
{
    cin>>n;

    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        cnta[++a[i]]++;
    }
    for(int i=1;i<N;i++) suma[i]=suma[i-1]+cnta[i];

    for(int i=1;i<=n;i++) cin>>b[i],b[i]++;

    for(int i=1;i<=n;i++)
    {
        cin>>c[i];
        cntc[++c[i]]++;
    }
    for(int i=1;i<N;i++) sumc[i]=sumc[i-1]+cntc[i];

    LL res=0;
    for(int i=1;i<=n;i++)
        res+=(LL)suma[b[i]-1]*(sumc[N-1]-sumc[b[i]]);
    cout<<res<<endl;

    //system("pause");
    return 0;
}
原文地址:https://www.cnblogs.com/fxh0707/p/14572541.html