树状数组+逆序数----+ 离散化思想

题目:

 

Description

一天ZYS闲着无聊,打算把实验的工具按照一定的顺序排列好,方便下一次做实验时找到对应的实验的工具。这天他正在整理电阻,PQ路过找ZYS去飙车。看到ZYS正在忙,于是问他,还需要多久,这时ZYS急了,他想知道 他还需要操作多久才能和PQ去飙车。阻值依次从低到高排序,每次只能交换相邻的电阻,设每次操作需要花费1min. ZYS想去飙车于是他请求你帮他计算一下至少花费多少min。蟹蟹
 

Input

输入

第一行:一个N (1<=N<=1e6)

第二行:ai电阻的阻值(a1、a2、a3 、…… 、an)(0<=ai<=1000 000 000)
 

Output

输出:

 

对于输入中的每个案例,生成一行‘cas  :n’,其中n是案例编号,从1开始。

 

对于输入中的每个案例,输出一行包含一个数字:需要的最少花费min。
 

Sample Input Copy

5
9 1 0 5 4
3
1 2 3
0

Sample Output Copy

cas  :1
6
cas  :2
0

题意与思路:

题意就是两个之间交换,问至少需要交换多少次才能使这个序列从小到大排序。

实际上就是求这一串数字得逆序数。而逆序数就是看这个数之前有多少个比该数大得数,例如

9 1 0 5 4   -------->   0 1 2 1 2 = = 6  

如果我们一个挨一个去比较的话,这是肯定会超出我们得时间,我们就要去想一个更方便快捷得方法,因为刚好学了树状数组,就直接想到要用到它。

先将  9 进入树状数组中,但是我们因为 直接放进树状数组9得那个位置里面去得话,我们看到输入得值是可能会达到1e9得,因此我们就要想到离散化,就要将此值的用位置来代替,如例题,9是最大的,我们直接用5 这个位置来表示9,进入树状的时候我们就想到去改变 d[ 9]的值

将数组离散化好之后,就从第一个值开始进入,从1->n依次进入,此时进入的是离散化后的值,比如a[1]  = 5, 这个5 就是 离散化的 9 ,然后送入树状数组 , 用当前 i(i表示的是原数组的位置)-  前面比它小的数的总和(利用树状数组求前缀和) == 前面比它大的数的总和 。因为当前的树状数组只有 d[9] 有值,因此  1 - 1 =0.  

又比如当入1 这个值的时候 2 - 1 = 1 (因为前缀和加了本身,所以现在有个1)

入 0 ----> 3 - 1 = 2

入 5 ----> 4 - 3 = 1

入 4-----> 5 - 3 = 2

#include<bits/stdc++.h>
using namespace std;

long long n,ans;
long long a[1000020];
long long d[1000020];
struct node
{
    long long  data,loc;
} ss[1000020];
bool cmp(node a,node b)
{
    return a.data<b.data;
}
void add(long long i,long long  v)
{
    while(i<=n)
    {
        d[i] += v;
        i+=(i&(-i));
    }
}
int findd(long long x)
{
    long long ans = 0;
    while(x>0)
    {
        ans+=d[x];
        x-=(x&(-x));
    }
    return ans;
};
int main()
{
    int k=0;
    while(~scanf("%lld",&n)&&n!=0)
    {
        k++;
        ans = 0;
        memset(a,0,sizeof(a));
        memset(d,0,sizeof(d));
        for(int i=1; i<=n; i++)
        {
            scanf("%lld",&ss[i].data);
            ss[i].loc = i;
        }
        sort(ss+1,ss+n+1,cmp);
        for(int i=1; i<=n; i++)
            a[ss[i].loc] = i;
        for(int i=1; i<=n; i++)
        {
            add(a[i],1);
            ans += i-findd(a[i]);
        }
        printf("cas  :%d\n",k);
        
        printf("%lld\n",ans);
    }
    return 0;
}

 

 

 

 

 

 

 

 

 

       

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值