• [蓝桥杯][历届试题]小朋友排队


    只通过比较和交换相邻两个数值的排序方法,实际上就是冒泡排序。在排序过程中每找到一对大小颠倒的相邻数值,把它们交换,就会使整个序列的逆序对个数减少(1)。最终排好序后的逆序对个数显然为(0),所以对序列进行冒泡排序需要的最少交换次数就是序列中逆序对的个数。

    本题要求计算出所有小朋友的不高兴程度之和,所有本题不是计算总的逆序对数,而是分别计算出每个位置上的数和序列中其他数所构成的逆序对数,从而得到每个位置上的不高兴程度,如果第(i)个位置和([0 sim i-1])([i+1 sim n-1])所构成的逆序对数为(k),则第(i)个位置的不高兴程度为(1 + 2 + cdots+k = frac{k(k+1)}{2})

    归并排序:

    const int N=1e5+10;
    PII a[N],temp[N];
    int cnt[N];
    int n;
    
    void merge(int l,int mid,int r)
    {
        int i=l,j=mid+1,k=0;
        while(i<=mid && j<=r)
        {
            if(a[i].fi <= a[j].fi)
            {
                cnt[a[i].se]+=j-mid-1;
                temp[k++]=a[i++];
            }
            else
            {
                cnt[a[j].se]+=mid-i+1;
                temp[k++]=a[j++];
            }
        }
    
        while(i<=mid)
        {
            cnt[a[i].se]+=j-mid-1;
            temp[k++]=a[i++];
        }
        while(j<=r)
            temp[k++]=a[j++];
        
        for(int i=0;i<k;i++) a[l+i]=temp[i];
    }
    
    void mergeSort(int l,int r)
    {
        if(l >= r) return;
    
        int mid=l+r>>1;
        mergeSort(l,mid);
        mergeSort(mid+1,r);
        merge(l,mid,r);
    }
    
    int main()
    {
        cin>>n;
    
        for(int i=0;i<n;i++)
        {
            cin>>a[i].fi;
            a[i].se=i;
        }
    
        mergeSort(0,n-1);
        
        LL res=0;
        for(int i=0;i<n;i++)
            res+=(LL)cnt[i]*(cnt[i]+1)/2;
            
        cout<<res<<endl;
        //system("pause");
        return 0;
    }
    

    树状数组:

    const int N=1e6+10;
    int c[N];
    int a[N];
    int cnt[N];
    int n;
    
    int lowbit(int x)
    {
        return x&-x;
    }
    
    void add(int x,int v)
    {
        for(int i=x;i<N;i+=lowbit(i))
            c[i]+=v;
    }
    
    int sum(int x)
    {
        int res=0;
        for(int i=x;i;i-=lowbit(i))
            res+=c[i];
        return res;
    }
    
    int main()
    {
        cin>>n;
    
        for(int i=0;i<n;i++) cin>>a[i],a[i]++;
    
        // 求每个数前面有多少个数比它大
        for(int i=0;i<n;i++)
        {
            cnt[i]+=sum(N-1)-sum(a[i]);
            add(a[i],1);
        }
    
        memset(c,0,sizeof c);
        // 求每个数后面有多少个数比它小
        for(int i=n-1;i>=0;i--)
        {
            cnt[i]+=sum(a[i]-1);
            add(a[i],1);
        }
    
        LL res=0;
        for(int i=0;i<n;i++)
            res+=(LL)cnt[i]*(cnt[i]+1)/2;
        cout<<res<<endl;
        //system("pause");
        return 0;
    }
    
  • 相关阅读:
    软工作业02
    进行代码复审训练
    源代码管理工具调查
    软工作业PSP与单元测试训练【任务一】
    15软工 15100340
    进行代码复审训练
    源代码管理工具调查
    软工作业PSP与单元测试训练
    软工课后作业001
    20180320作业2:进行代码复审训练
  • 原文地址:https://www.cnblogs.com/fxh0707/p/14587933.html
Copyright © 2020-2023  润新知