最接近神的人_NOI导刊2010提高
题目描述
破解了符文之语,小FF开启了通往地下的道路。当他走到最底层时,发现正前方有一扇巨石门,门上雕刻着一幅古代人进行某种活动的图案。而石门上方用古代文写着“神的殿堂”。小FF猜想里面应该就有王室的遗产了。但现在的问题是如何打开这扇门……
仔细研究后,他发现门上的图案大概是说:古代人认为只有智者才是最容易接近神明的。而最聪明的人往往通过一种仪式选拔出来。仪式大概是指,即将隐退的智者为他的候选人写下一串无序的数字,并让他们进行一种操作,即交换序列中相邻的两个元素。而用最少的交换次数使原序列变成不下降序列的人即是下一任智者。
小FF发现门上同样有着n个数字。于是他认为打开这扇门的秘诀就是找到让这个序列变成不下降序列所需要的最小次数。但小FF不会……只好又找到了你,并答应事成之后与你三七分……
输入输出格式
输入格式:
第一行为一个整数n,表示序列长度
第二行为n个整数,表示序列中每个元素。
输出格式:
一个整数ans,即最少操作次数。
输入样例#1:
4
2 8 0 3
输出样例#1:
3
样例说明:开始序列为2 8 0 3,目标序列为0 2 3 8,可进行三次操作的目标序列:
1.Swap (8,0):2 0 8 3
2.Swap (2,0):0 2 8 3
3.Swap (8,3):0 2 3 8
说明
对于30%的数据1≤n≤10^4。
对于100%的数据1≤n≤5*10^5;
-maxlongint≤A[i]≤maxlongint。
分析:树状数组记录第i位前面比它小的数个数,那么就可以知道第i位前面有多少数比它大,即需要交换的次数,累加一下就好了。
代码
#include <cstdio>
#include <algorithm>
#define N 500005
#define ll long long
using namespace std;
struct arr
{
int x,pos;
}a[N];
ll c[N],ans;
int n;
int so(arr p,arr q)
{
if (p.x==q.x) return p.pos<q.pos;
return p.x<q.x;
}
ll sum(int x)
{
ll s=0;
while (x>0)
{
s+=c[x];
x-=x&(-x);
}
return s;
}
void change(int x)
{
while (x<=n)
{
c[x]++;
x+=x&(-x);
}
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i].x);
a[i].pos=i;
}
sort(a+1,a+n+1,so);
for (int i=1;i<=n;i++)
{
change(a[i].pos);
ans+=i-sum(a[i].pos);
}
printf("%lld",ans);
}