1009. Triple Inversions (35)

亡梦爱人 提交于 2019-12-02 11:39:01

PAT上一道题目。题意是在一个1~n组成的n元数组A中,计算三元逆序的个数(满足i<j<k,Ai>Aj>Ak的(Ai,Aj,Ak))的个数。
所有的三元逆序对,从中间数Aj考虑。对每一个数Ap,如果知道在p之前比Ap大的个数leftBigger以及在p 之后比Ap小的个数rightSmall。那么以Aj作为中间数的三元逆序对个数为leftBigger*rightSmall。对每一个p,计算以Ap为中间数的三元逆序,累加求和即可。计算leftBigger的方法可以采用树状数组。我们可以反过来看,已知Ap的位置p的情况下,计算leftBigger可以等价于计算leftSmall。leftBigger(Ap) = p-1-leftSmall(Ap)。先构建如下场景:
1.构建一个辅助数组a(1)~a(n).初始值均为0;
2.从左到右遍历A1~An.当遍历到Ap时,修改a(Ap) = 1;此时a(1)~a(Ap-1)值为1的个数即为在Ap的leftSmall(Ap),leftSmall(Ap) = sum(a(1)~a(Ap-1)).树状数组可以在lg(n)时间内计算出数组区间内元素之和。
代码如下:

#include<iostream>
#include<vector>
using namespace std;
int main()
{
    int n;
    cin>>n;
    vector<int> nums(n+1);
    for(int i=1;i<=n;++i)
    {
        cin>>nums[i];
    }
    vector<int> c(n+1,0);
    vector<int> leftSmall(n+1,0);
    for(int i=1;i<=n;++i)
    {
        int x = nums[i]-1;
        while(x>0){
            leftSmall[nums[i]]+=c[x];
            x-=x&(-x);
        }
        x = nums[i];
        while(x<=n){
            c[x]++;
            x+=x&(-x);
        }
    }
    long long ans = 0;
    for(int i=1;i<=n;++i)
    {
        long long leftBigger=0,rightSmall = 0;
        leftBigger = i-leftSmall[nums[i]]-1;
        rightSmall = nums[i] -1-leftSmall[nums[i]];
        ans+=leftBigger*rightSmall;
    }
    cout<<ans<<endl;
    return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!