hdu1394(线段树求逆序对)

题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=1394

线段树功能:update:单点增减 query:区间求和

分析:如果是0到n-1的排列,那么如果把第一个数放到最后,对于这个数列,逆序数是减少a[i],而增加n-1-a[i]的,所以每次变化为res+=n-a[i]-1-a[i].

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
#define maxn 5555
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
int sum[maxn<<2];
int a[maxn];
void PushUp(int rt)
{
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void build(int l,int r,int rt)
{
    sum[rt]=0;
    if(l==r)
        return;
    int m=(r+l)>>1;
    build(lson);
    build(rson);
}
void update(int pos,int l,int r,int rt)
{
    if(r==l)
    {
        sum[rt]++;
        return;
    }
    int m=(r+l)>>1;
    if(pos<=m)update(pos,lson);
    else update(pos,rson);
    PushUp(rt);
}
int Query(int L,int R,int l,int r,int rt)
{
    if(L<=l&&r<=R)
    {
        return sum[rt];
    }
    int m=(r+l)>>1;
    int res=0;
    if(L<=m)res+=Query(L,R,lson);
    if(m<R)res+=Query(L,R,rson);
    return res;
}
int main()
{
    int n;
    while(scanf("%d",&n)>0)
    {
        build(0,n-1,1);
        int sum=0;
        for(int i=0;i<n;i++)
        {
            scanf("%d",&a[i]);
            sum+=Query(a[i],n-1,0,n-1,1);
            update(a[i],0,n-1,1);
        }
        int ans=sum;
        for(int i=0;i<n;i++)
        {
            sum+=n-a[i]-a[i]-1;
            ans=min(ans,sum);
        }
        printf("%d\n",ans);
    }
    return 0;
}

树状数组求逆序对更简单。。。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
#define maxn 5555
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
int a[maxn],c[maxn];
int n;
int lowbit(int x)
{
    return x&(-x);
}
int sum(int i)
{
    int res=0;
    while(i)
    {
        res+=c[i];
        i-=lowbit(i);
    }
    return res;
}
void update(int i,int x)
{
    while(i<=n)
    {
        c[i]+=x;
        i+=lowbit(i);
    }
}
int main()
{
    while(scanf("%d",&n)>0)
    {
        int res=0;
        memset(c,0,sizeof(c));
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            a[i]++;
            res+=i-1-sum(a[i]);//求1~a[i]区间的出现的数,再总(i-1)-sum(a[i])即可
            update(a[i],1);//单点更新
        }
        int ans=res;
        for(int i=1;i<=n;i++)
        {
            res+=n-a[i]-(a[i]-1);
            ans=min(ans,res);
        }
        printf("%d\n",ans);
    }
}

时间: 2024-10-15 10:52:25

hdu1394(线段树求逆序对)的相关文章

BNU 2418 Ultra-QuickSort (线段树求逆序对)

题目链接:http://acm.bnu.edu.cn/bnuoj/problem_show.php?pid=2418 解题报告:就是给你n个数,然后让你求这个数列的逆序对是多少?题目中n的范围是n < 500000,所以,暴力是不行的.还是第一次学会用线段树求逆序数,这种方法的时间复杂度是n * log n,是不是很快呢,利用了线段树查询速度快的优势.具体的方法如下: 这里先说一下,如果输入的n个数不是连续的,也就是说把这n个数按从小到大的顺序排列起来不是连续的话,还要先离散化一下,其实也就是把

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

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1394 解题报告:给出一个序列,求出这个序列的逆序数,然后依次将第一个数移动到最后一位,求在这个过程中,逆序数最小的序列的逆序数是多少? 这题有一个好处是输入的序列保证是0 到 n-1,所以不许要离散化,还有一个好处就是在计算在这个序列中比每个数大和小的数一共有多少个的时候可以在O(1)时间计算出来,一开始我没有意识到,还傻傻的用了两层for循环来每次都计算,当然这样果断TLE了.把一个数从第一个移

权值线段树求逆序对问题

我们都知道,求逆序对数量可以用归并排序解决.但是用归并排序只能解决静态的序列问题,没有扩展的区间.因此就有了用权值线段树求逆序对的方法. 1 #include<iostream> 2 #include<iomanip> 3 #include<ctime> 4 #include<climits> 5 #include<algorithm> 6 #include<queue> 7 #include<vector> 8 #inc

hdoj 1394 Minimum Inversion Number【线段树求逆序对】

求逆序对有很多算法,这里说一下线段树求逆序对的思想. 知识点:线段树,逆序对,单点更新,成段求和 算法:线段树求逆序数的前提条件是要离散化,变成连续的点,首先建树,每个节点设置一个num值为0. 然后根据逆序对的定义,前面出现过的比当前数大的个数的和,我们需要求前面的比他大的数,其实就相当于从当前a[i]点对他后面所有出现过的数求和一次.然后把当前点的值在线段树叶子节点变为1,表示出现过,并向上更新到线段树里面.比如说样例4 2 1 5 3 首先4后面没有值,4更新为1,4--5区间更新为1,1

HDU 4911 http://acm.hdu.edu.cn/showproblem.php?pid=4911(线段树求逆序对)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4911 解题报告: 给出一个长度为n的序列,然后给出一个k,要你求最多做k次相邻的数字交换后,逆序数最少是多少? 因为每次相邻的交换操作最多只能减少一个逆序对,所以最多可以减少k个逆序对,所以我们只要求出原来的序列有多少个逆序对然后减去k再跟0取较大的就可以了. 因为数据范围是10的五次方,所以暴力求肯定会TLE,所以要用n*logn算法求逆序对,n*logn算法有几种可以求逆序对的: 线段树,树状数

线段树求逆序对

嘘!这里是逆序对的题目链接 以前一直不知道线段树有求逆序对的功能 之前老师提了一下又刚好没听 今天自己模拟了一遍似乎是对了 代码虽短但耗费的空间却大 而归并排序代码虽然复杂却只耗费少量的空间 大概思想: 1.建一棵和最大数值一样大的线段树 2.每次在树中查找这个点的位置 3.在查找的过程中有两种选择 (1).往左子树下去 这时需要将当前节点的值减去左节点的值 (2).往右子树下去 不用操作 4.进入子树,直到遇到这个值所在的区间(l=r=a)的时候 5.将这个区间的值加1,如何维护树 1 #in

【权值线段树】离散化介绍 (+利用 线段树 求逆序对)

先介绍一下离散化 桶排大家应该知道,就是开一个数组(下标为数值,记录了该数值的出现次数)然后遍历过去如果出现次数不为零,那就输出这些数字,理论时间复杂度可以达到O(N)但是由于内存限制,不能开很大的数组. 然而 如果某个数列中的数字不要求大小确定,只要求这些数字有相对的大小就够了的话,离散化就有了用武之地 举个例子:数列 3 8 7 5 2000000000000000 我们发现有几个数之间差距很大,但是我们用不到数值的大小,只要求相对大小,那怎么办呢? 观察下面的数列: 1 4 3 2 5 真

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

HDU 1394 题意: 给一个由0~n-1组成的序列,求出该序列的所有循环同构序列中的最小逆序对数目,逆序对的两个元素可以不相邻. 思路: 这题据说可以直接暴力O(n2)可以水过.. 说一下线段树做法O(nlogn): 以这个序列来说明: 1,9,2,3,0,8,5,7,4,6 我们先假设有一个长度为n元素全为0的数组: 0,0,0,0,0,0,0,0,0,0 我们先计算所给序列的第一项1(实际上是第0项)的数字所对应位置之后所有元素的和(括号里面的数),和就是当前与这个数逆序的数的个数,这样

线段树求逆序数方法 HDU1394&amp;&amp;POJ2299

为什么线段树可以求逆序数? 给一个简单的序列 9 5 3 他的逆序数是3 首先要求一个逆序数有两种方式:可以从头开始往后找比当前元素小的值,也可以从后往前找比当前元素大的值,有几个逆序数就是几. 线段树就是应用从后往前找较大值得个数.(一边更新一边查) 当前个数是 n = 10 元素   9  5   3 9先加入线段树,T[9]+=1:查从T[9]到T[10]比9大的值,没有sum = 0: 5 加入线段树,T[5] += 1,查从T[5]到T[10]比5大的值,有一个9,sum +=1: 3