HDU1394 线段树和逆序对 xingxing在努力

   蛮好的一道线段树和逆序对结合的题, 考虑到从前面拿掉一个数放到后面对原序列的影响有两点:1:被拿掉的数后面所有比这个数小的逆序数减1 2:当被拿掉的数放到原序列的末尾之后序列的逆序对增加被拿掉的数前面比这个数大的数的个数  根据上面两点我们可以推出一个公式 new = old - a1后面比a1小的数的个数 + a1后面比a1大的数的个数。

old为原序列的逆序数, nw为新序列的逆序数。代码如下:

 

  

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
const int maxn = 5000 + 10;

struct segment
{
    int l, r;
    int sum;         //区间和
}seg[3*maxn];

void build(int rt, int l, int r)
{
    seg[rt].l = l;
    seg[rt].r = r;
    seg[rt].sum = 0;
    if(l==r) return ;
    int chl = 2*rt, chr = 2*rt + 1;
    build(chl, l, (l+r)/2);
    build(chr, (l+r)/2+1, r);
}

void update(int rt, int q)   //将q结点置1
{
    if(seg[rt].l == seg[rt].r)
    {
        seg[rt].sum = 1;
        return ;
    }
    int mid = (seg[rt].l + seg[rt].r)/2;
    if(q <= mid)
        update(2*rt, q);
    else
        update(2*rt+1, q);
    seg[rt].sum = seg[2*rt].sum+seg[2*rt+1].sum;
}

int sum(int rt, int l, int r)
{
    if(l > r) return 0;
    if(seg[rt].l==l && seg[rt].r==r)
    {
        return seg[rt].sum;
    }
    int mid = (seg[rt].l+seg[rt].r)/2;
    if(r <= mid)
        return sum(2*rt, l, r);
    else if(l>mid)
        return sum(2*rt+1, l, r);
    else
    {
        int v1 = sum(2*rt, l, mid);
        int v2 = sum(2*rt+1, mid+1, r);
        return v1+v2;
    }
}

int a[maxn];

int main()
{
    int n;
    while(scanf("%d", &n) == 1)
    {
        for(int i=1; i<=n; i++)
        {
            scanf("%d", &a[i]);
            a[i]++;
        }
        build(1, 1, n);
        int inver = 0;
        for(int i=1; i<=n; i++)                  //求出原序列的逆序数
        {
            update(1, a[i]);
            inver += sum(1, a[i]+1, n);
        }
        int res = inver;
        int old = inver;
        for(int i=1; i<n; i++)
        {
            int nw = old-(a[i]-1)+(n-a[i]);
            res = min(res, nw);
            old = nw;
        }
        printf("%d\n", res);
    }
    return 0;
}

 

posted @ 2015-11-21 13:53  xing-xing  阅读(109)  评论(0)    收藏  举报