• 2019.3.16数据结构考试(Problem 1. rotinv)(循环逆序对)


    大概意思呢,就是求循环序列的逆序对

    题解中有这样的一个思考过程

    1.1 30%
    O(n3) 暴力:枚举每个循环状态,再暴力计算逆序对数。

    #include <cstdio>
    #include <iostream>
    using namespace std;
    const int N = 1000000 + 10;
    int n;
    int aa[N + N];
    int main() {
        scanf( "%d", &n );
        for( int i = 1; i <= n; i++ ) {
            scanf( "%d", aa + i );
            aa[n + i] = aa[i];
        }
        long long ans = 0;
        for( int i = 1; i <= n; i++ ) {
            for( int l = 0; l < n; l++ )
                for( int r = l + 1; r < n; r++ )
                    ans += (aa[i+l] > aa[i+r]);
        }
        cout << ans << endl;
    }
    30分暴力

    1.2 60%
    法一:

    将原序列复制一份接在自己后面,形成一个长度为2n 的序列,先暴力计算[1,n] 的逆序对数,然后将这个区间向右边移动,维护答案。具体来说,就是假如我们算出了[i,j] 中的逆序对数,我们怎么算[i + 1, j + 1] 呢,其实就是加上[i+1, j] 中比a[j +1] 大的数的个数再减去[i+1, j] 中比a[i] 小的数的个数。所以我们可以暴力算那两个数量,连续n 个长度为n 的子区间的逆序数的和就是答案,复杂度O(n2)。
    法二:

    计算一个长度为n 的序列的逆序对的个数可以通过数据结构(树状数组或线段树)优化到O(nlogn),维护[1,i - 1] 的一个值的分布情况(加入到线段树中),每次查询这些数比a[i] 大的有多少个,再将a[i] 加入到线段树中去。所以计算n 个序列的逆序数只需要O(n2logn).
    1.3 100%
    将上面的法一和法二结合一下,用线段树优化法一中那个暴力,使得可以O(logn) 查询,最后复杂度O(nlogn).

    #include<bits/stdc++.h>
    #define N 1000003
    #define ll long long
    using namespace std;
    ll a[N*2],n,c[N*4];
    ll lowbit(int n){return n&(-n);}
    void update(ll x,ll k)
    {
        while(x<=n)
        {
            c[x]+=k;
            x+=lowbit(x);
        }
    }
    ll query(ll x)
    {
        ll ans=0;
        while(x)
        {
            ans+=c[x];
            x-=lowbit(x);
        }
        return ans;
    }
    //树状数组里存的是1~n每个数的个数(其实就是树状数组求逆序对+区间移动的思想)
    //对于一个数a[i],可以query它的位置query(a[i])-1就是前面比他小的数的个数,
    //那么前面就有i-query[a[i]]个比它小的数 
    int main()
    {
    //    freopen("rotinv.in","r",stdin);
    //    freopen("rotinv.out","w",stdout);
        int m;
        ll ans=0,cur=0;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%I64d",&a[i]);
            a[n+i]=a[i];
        }
        for(int i=1;i<=n;i++)
        {
            ans+=(i-1)-query(a[i]);//i-1是因为这个时候只存了i-1个数,第i个数还没存la 
            update(a[i],1);//将a[i]的个数加1 
        }
        for(int i=n+1;i<=2*n;i++)
        {
            update(a[i-n],-1);//区间移动,a[i-n]已经不在区间内了,
            ans+=(n-1)-query(a[i]);//那么就要把因为a[i-n]存在而失去的贡献加上
            ans-=query(a[i-n]-1);//减去a[i-n]带来的贡献(这里找的是比a[i-n]小的数的个数,比其小才能构成逆序对)
            update(a[i],1);
            cur+=ans;//ans是每个序列的答案,最终的答案是每个序列的ans加起来 
        }
        printf("%I64d",cur);
        
    }
    /*
    5 4
    1 3 2 4 2
    1 4
    2 4
    1 3
    2 3
    */
    AC(树状数组)

    当然逆序对还可以用归并排序求,再以类似的思想循环序列:相当于每次把队首踢到队尾,那么与它有关的贡献就会更新

    代码很好理解~

    #include<bits/stdc++.h>
    using namespace std;
    #define N 1000005
    #define ll long long
    int n,sma[N],big[N];
    ll tot=0;
    void merge(int a[],int l,int r,int mid,int b[])
    {
        memcpy(b+l,a+l,sizeof(int)*(r-l+1));
        int i=l,k=l,j=mid+1;
        while(i<=mid&&j<=r) 
        {
            if(b[i]<=b[j])
              a[k++]=b[i++];
             else
              a[k++]=b[j++],tot+=(mid-i+1);
        }
        
        while(i<=mid) a[k++]=b[i++];
        while(j<=r)   a[k++]=b[j++];
    }
    void merge_sort(int a[],int l,int r,int b[])
    {
        if(l<r)
        {
            int mid=(l+r)>>1;
            merge_sort(a,l,mid,b);
            merge_sort(a,mid+1,r,b);
            merge(a,l,r,mid,b);
        }
    }
    //上面都是归并排序 
    int a[N<<1],b[N<<1],c[N<<1];
    ll ans=0;
    int main()
    {
    //    freopen("rotinv.in","r",stdin);
    //    freopen("rotinv.out","w",stdout);
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
         scanf("%d",&a[i]),c[i]=a[i];
        merge_sort(a,1,n,b);
        //排序后再分别找出序列中比a[i]小,大的个数 
        for(int i=2;i<=n;i++)
        if(a[i]!=a[i-1])
         sma[a[i]]=i-1;
        for(int i=n-1;i>=1;i--)
        if(a[i]!=a[i+1])
         big[a[i]]=n-i;
        for(int i=1;i<=n;i++)//不断的踢出每一个c[i] 
        {
            ans+=tot;
            tot-=sma[c[i]];
            tot+=big[c[i]];
        }
        printf("%I64d
    ",ans);
    }
    归并排序

    完成lalala~

  • 相关阅读:
    CentOS7- 配置阿里镜像源
    MySQL学习笔记
    OSI&TCP/IP模型
    加密算法学习
    golang学习笔记
    Redis学习总结整理
    TCP
    HTTP/HTTPS
    HTTP2
    MVCC
  • 原文地址:https://www.cnblogs.com/yyys-/p/10561768.html
Copyright © 2020-2023  润新知