hdu 1394(线段树求逆序数)

来源:互联网 发布:琢本网络 编辑:程序博客网 时间:2024/06/05 15:40

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


题意描述:给你一个有0--n-1数字组成的序列,然后进行这样的操作,每次将最前面一个元素放到最后面去会得到一个序列,那么这样就形成了n个序列,那么每个序列都有一个逆序数,找出其中最小的一个输出!


分析:其实只需求出一个序列的逆序数即可,一位第i+1个序列的逆序数为第i的序列逆序数+(n-a[i]-1)-a[i],那么找出其中最小的即可,现在的问题就是求逆序数?求逆序数的方法很多,数据小的时候可以直接两重循环暴力,可以用归并排序,树状数组,线段树!这里讲一下线段树:


由于该题比较特殊数据都是连续且离散的,那么直接就可以用线段树求解,假如区间不是连续的或者是非离散的题目,那么就需要先离散化处理一下!!线段树求逆序数的思想,按输入顺序插入元素,在插入元素之前先搜索比该元素大的数已经出现的个数即查找区间(a[i],n-1),查找出来的个数就是需要增加的逆序数,之后我们就更新区间来改变区间内已经出现的点的个数、废话半天,代码关键:



#include<iostream>#include<cstdio>#include<cstring>using namespace std;const int N = 5050;struct node{int l,r;int num; //表示该区间已经出现节点的个数}tree[N*4];void bulid(int rt ,int l,int r){tree[rt].l=l;tree[rt].r=r;tree[rt].num=0;if(l==r)return;int mid = (l+r)/2;bulid(2*rt,l,mid);//创建左子树bulid(2*rt+1,mid+1,r);}int search(int rt, int l,int r){if(l>r)return 0;if(l==tree[rt].l&&r == tree[rt].r)return tree[rt].num;int mid = (tree[rt].l+tree[rt].r)/2;if(r<=mid)return search(2*rt,l,r);else if(l>mid)return search(2*rt+1,l,r);elsereturn search(2*rt,l,mid)+search(2*rt+1,mid+1,r);}void update(int rt, int x){tree[rt].num++;if(tree[rt].l==tree[rt].r)return;int mid = (tree[rt].l+tree[rt].r)/2;if(x<=mid)update(2*rt,x);else update(2*rt+1,x);}/*void print(int rt){printf("%d %d %d\n",tree[rt].l, tree[rt].r , tree[rt].num);if(tree[rt].l==tree[rt].r)return;print(2*rt);print(2*rt+1);}*/int main (){int a[N];int n;int i, min;while(scanf("%d",&n)!=EOF){bulid(1,0,n-1);int sum = 0;for(i=0;i<n;i++)    scanf("%d",&a[i]);for(i=0;i<n;i++){  sum+=search(1,a[i]+1,n-1);//查找在a[i]之前出现且比a[i]大的数//cout << sum << endl;update(1,a[i]);}min =sum;for(i=0;i<n-1;i++){sum += (n-a[i]-1)-a[i];if(min>sum)min =sum;}printf("%d\n",min);}return 0;}


原创粉丝点击