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

时间:2024-04-17 06:59:59

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

解题报告:给出一个序列,求出这个序列的逆序数,然后依次将第一个数移动到最后一位,求在这个过程中,逆序数最小的序列的逆序数是多少?

这题有一个好处是输入的序列保证是0 到 n-1,所以不许要离散化,还有一个好处就是在计算在这个序列中比每个数大和小的数一共有多少个的时候可以在O(1)时间计算出来,一开始我没有意识到,还傻傻的用了两层for循环来每次都计算,当然这样果断TLE了。把一个数从第一个移动到最后一个的时候逆序数的变化就等于原来的逆序数加上这个序列中所有比这个数大的数的个数再减去所有比这个数小的个数。

 #include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = +;
int que[maxn];
struct node
{
int data,l,r;
}tree[*maxn];
void maketree(int p)
{
if(tree[p].l == tree[p].r)
return ;
int mid = (tree[p].l + tree[p].r) / ;
tree[*p].data = ;
tree[*p].l = tree[p].l;
tree[*p].r = mid;
maketree(*p);
tree[*p+].data = ;
tree[*p+].l = mid + ;
tree[*p+].r = tree[p].r;
maketree(*p+);
}
int find(int p,int l,int r)
{
if(l > r)
return ;
if(tree[p].l == l && tree[p].r == r)
return tree[p].data;
int mid = (tree[p].l + tree[p].r) / ;
if(r <= mid)
return find(*p,l,r);
if(l <= mid && r > mid)
return find(*p,l,mid) + find(*p+,mid +,r);
else return find(*p+,l,r);
}
void push(int p,int d)
{
tree[p].data++;
if(tree[p].l == tree[p].r)
return ;
int mid = (tree[p].l + tree[p].r) / ;
if(d <= mid)
push(*p,d);
else push(*p+,d);
}
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
for(int i = ;i < n;++i)
scanf("%d",&que[i]);
tree[].data = ;
tree[].l = ;
tree[].r = n-;
maketree();
int tot = ;
for(int i = ;i < n;++i)
{
tot += find(,que[i] + ,n-);
push(,que[i]);
}
int ans = tot;
for(int i = ;i < n;++i)
{
tot = tot - que[i] + n - que[i] - ;
ans = min(tot,ans);
}
printf("%d\n",ans);
}
return ;
}