题目:
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;
}