hdu 1394 求逆序数(线段树求)

来源:互联网 发布:树莓派和单片机的区别 编辑:程序博客网 时间:2024/06/05 14:12

http://acm.hdu.edu.cn/showproblem.php?pid=1394


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


解析:

求出a1, a2, ..., an-1, an的逆序数之后,就可以递推求出其他序列的逆序数。 假设要把a1移动到an之后,那么我们把这个过程拆分成两步:

1.  把a1去除掉。通过观察可以发现,(a1-1)是0~n-1中比a1小的数字的个数,由于a1在序列的第一个所以a1之后共有(a1-1)个比a1小,所以形成了(a1-1)对逆序数,当去除掉a1时,原序列的逆序数总数也就减少了(a1-1)个逆序数。 
2. 把a1加到an之后。0~n-1中,比a1大的数共有(n-a1)个数,由于a1现在在最后一个,也就是它前面共有(n-a1)个数比它大,即增加了(n-a1)对逆序数。
综合1,2两步, 设原序列逆序数为sum, 当把原序列第一个移动到最后位置时,逆序数变为:sum = sum-(ai-1)+(n-ai);

3.由于此题范围是0到n-1;所以sum = sum -(ai) + (n - 1 - ai);


#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);      else          return 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;  }    


0 0
原创粉丝点击