HDU 1394 Minimum Inversion Number (树状数组)

依旧是再练习下树状数组的使用:

题目大意:   给出N个数,这些数可以把后面的删掉然后放到最前面形成新的序列

可得到的N种情况,求出这N种情况哪种的逆序数最小

解题思路:   先求出第一个序列的逆序数,然后用很巧妙的办法求下一个序列的逆序数,直到全部求出

序列 4 5 2 1 3 6 ,此序列的逆序数为7,它等到的下一个序列为 5 2 1 3 6 4

看这个新序列的产生过程,首部删除4,尾部添加4

删除4,必然会使得这个序列的逆序数减少(4-1)个,因为4前面必定有4-1个数小于4

添加4,必然会使得这个序列的逆序数增加(6-4)个,因为4后面必定有6-4个数大于4

由此推出公式,假设移动的数为m,序列的逆序数=上一序列逆序数-(m-1)+(N-m)

先要离散化出来(这点似乎不需要,但是我还是离散了),因为给的数据范围就是0~n-1

<pre name="code" class="cpp">//树状数组
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
int n;
#define MAX 5010
typedef struct nano{
    int val;
    int order;
}node;
node a[MAX];
int aa[MAX];
int c[MAX];
int cmp(node a,node b){
    return a.val<b.val;
}
void update(int x){
    while(x<=n){
        c[x]++;
        x+=(x&-x);
    }
}
int sum(int x){
    int s=0;
    while(x>=1)//一定是1!0就死循环了
    {
        s+=c[x];
        x-=(x&-x);
    }
    return s;
}
int main(int argc, char *argv[])
{
   // freopen("1394.in","r",stdin);
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=1;i<=n;++i)
        {
            scanf("%d",&a[i].val);
            a[i].order=i;
        }
        /*for(int i=1;i<=n;++i)
          printf("%d ",a[i].val);*/
        sort(a+1,a+n+1,cmp);
        //printf("\n");
        for(int i=1;i<=n;++i)
            aa[a[i].order]=i;
        long long ans=0;
        /* printf("------\n");
        for(int i=1;i<=n;++i)
        {
            printf("%d ",aa[i]);
            }*/
        //printf("\n------\n");
        memset(c,0,sizeof(c));
        for(int i=1;i<=n;++i){
            update(aa[i]);
            ans+=(i-sum(aa[i]));
        }
        long long MIN=ans;
        for(int i=1;i<=n;++i){
            ans=ans-(aa[i]-1)+(n-aa[i]);
            if(ans<MIN)MIN=ans;
        }
        printf("%lld\n",MIN);
    }
    return 0;
}

至于速度上,跟前面的归并排序解得耗时一样,可能数据还是小了,没差别,但是在数据范围很大的时候,利用离散化还是很有用的,毕竟限于内存限制,不能一下子开那么大的数据空间。

时间: 2024-10-14 00:57:10

HDU 1394 Minimum Inversion Number (树状数组)的相关文章

HDU 1394 Minimum Inversion Number 树状数组&amp;&amp;线段树

题目给了你一串序列,然后每次 把最后一个数提到最前面来,直到原来的第一个数到了最后一个,每次操作都会产生一个新的序列,这个序列具有一个逆序数的值,问最小的你逆序数的值为多少 逆序数么 最好想到的是树状数组,敲了一把很快,注意把握把最后一个数提上来对逆序数的影响即可, #include<iostream> #include<cstdio> #include<list> #include<algorithm> #include<cstring> #i

HDU 1394 Minimum Inversion Number (树状数组求逆序数)

Minimum Inversion Number Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 13942    Accepted Submission(s): 8514 Problem Description The inversion number of a given number sequence a1, a2, ..., a

HDU 1394 Minimum Inversion Number (树状数组求逆序对)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1394 题目让你求一个数组,这个数组可以不断把最前面的元素移到最后,让你求其中某个数组中的逆序对最小是多少. 一开始就求原来初始数组的逆序对,树状数组求或者归并方法求(可以看<挑战程序设计>P178),然后根据最前面的元素大小递推一下每次移到最后得到的逆序数,取最小值. 1 #include <iostream> 2 #include <cstdio> 3 #include

[hdu1394]Minimum Inversion Number(树状数组)

Minimum Inversion Number Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 18395    Accepted Submission(s): 11168 Problem Description The inversion number of a given number sequence a1, a2, ..., a

hdu 1394 Minimum Inversion Number (裸树状数组 求逆序数)

题目链接 题意: 给一个n个数的序列a1, a2, ..., an ,这些数的范围是0-n-1, 可以把前面m个数移动到后面去,形成新序列:a1, a2, ..., an-1, an (where m = 0 - the initial seqence)a2, a3, ..., an, a1 (where m = 1)a3, a4, ..., an, a1, a2 (where m = 2)...an, a1, a2, ..., an-1 (where m = n-1)求这些序列中,逆序数最少的

HDU 1394 Minimum Inversion Number(线段树求最小逆序数对)

HDU 1394 Minimum Inversion Number(线段树求最小逆序数对) ACM 题目地址:HDU 1394 Minimum Inversion Number 题意: 给一个序列由[1,N]构成,可以通过旋转把第一个移动到最后一个. 问旋转后最小的逆序数对. 分析: 注意,序列是由[1,N]构成的,我们模拟下旋转,总的逆序数对会有规律的变化. 求出初始的逆序数对再循环一遍就行了. 至于求逆序数对,我以前用归并排序解过这道题:点这里. 不过由于数据范围是5000,所以完全可以用线

hdu 1394 Minimum Inversion Number 线段树 点更新

// hdu 1394 Minimum Inversion Number 线段树 点更新 // // 典型线段树的单点更新 // // 对于求逆序数,刚开始还真的是很年轻啊,裸的按照冒泡排序 // 求出最初始的逆序数,然后按照公式递推,结果就呵呵了 // // 发现大牛都是用线段树和树状数组之类的做的,而自己又在学 // 线段树,所以就敲了线段树. // // 线段树的节点保存一段区间( L,R )内0,1...n一共出现了多少个. // 因为每个数是0,1,2...n-1且没有重复的数字. /

HDU 1394 Minimum Inversion Number.(线段树)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1394 ~~~~ 早起一发线段树,开心又快乐.这题暴力也能水过,同时线段树的效率也就体现的尤为明显了,看了大牛的博客,说是还可以用树状数组,点树和合并序列写,现在还不懂,留着以后在写吧. ~~~~ 大致题意:给定一个数字序列,同时由此可以得到n个序列, 要求从n个序列中找到逆序数最小的序列,输出最小逆序数. 首先介绍下逆序数的概念: 在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面

hdu 1394 Minimum Inversion Number(线段树)

Minimum Inversion Number Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 10853    Accepted Submission(s): 6676 Problem Description The inversion number of a given number sequence a1, a2, ..., a