hdu1394 Minimum Inversion Number ——线段树入门题

April 23, 2013
Hdu Data Structure

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1394 题目大意:   给一个数字由0~n-1这n个数字组成的数列,不断地把第一个数字移动到最后,一共得到n个数列。求这n个数列中,逆序数最小是多少。 题目思路:   首先,建一棵线段树,每个节点表示这个区间内已经插入的数字的个数,开始初始化为0.然后没读入一个数字,把这个数字插入得到线段树的叶子节点,然后向上更新父节点。这样,在建树的过程中,就可以统计出每个逆序数,也就是说,可以再插入每个数字的时候,查找已经插入的数字当中,比这个数字大的数字有多少个,直到最后就可以求出这个数列的逆序数。   然后,利用数列的性质。因为每次都是把第一个数字移动到最后,比如这个数字是a,那么显然,比这个数字小的有a个,比这个数字大的有n-1-a个;因为这个数字在最前面,所以当前这个数字的逆序数是a,把这个数字移动到最后之后,这个数字的逆序数是n-1-a,逆序数增加量:n-1-a-a。这样就可以由原来的数列的逆序数求出所有数列的逆序数。好神奇~


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <cmath>
#include <algorithm>
#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1
using namespace std;
typedef long long int LL;
const int MAXN =  0x3f3f3f3f;
const int  MIN =  -0x3f3f3f3f;
const double eps = 1e-9;
const int dir[8][2] = {{0,1},{1,0},{0,-1},{-1,0},{-1,1},
  {1,1},{1,-1},{-1,-1}};
const int MAX = 5000+10;
int a[MAX<<2], n, b[MAX];
void pushup(int rt)
{
  a[rt] = a[rt<<1] + a[rt<<1|1];
}
void build(int l, int r, int rt)
{
  if (l == r) { a[rt] = 0; return; }
  int m = (l + r) >> 1;
  build(lson); build(rson); pushup(rt);
}
void update(int p, int l, int r, int rt)
{
  if (l == r) { a[rt]++; return; }
  int m = (l + r) >> 1;
  if (p <= m) update(p, lson);
  else update(p, rson);
  pushup(rt);
}
int query(int L, int R, int l, int r, int rt)
{
  if (L <= l && R >= r) { return a[rt]; }
  int m = (l + r) >> 1, ret = 0;
  if (L <= m) ret += query(L, R, lson);
  if (R > m) ret += query(L, R, rson);
  return ret;
}
int main(void){
#ifndef ONLINE_JUDGE
  freopen("hdu1394.in", "r", stdin);
#endif
  while (~scanf("%d", &n)){
    int i, j, sum; build(0, n - 1, 1);
    sum = 0;
    for (i = 0; i < n; ++i){
      scanf("%d", b + i);
      sum += query(b[i]+1, n - 1, 0, n - 1, 1);
      update(b[i], 0, n - 1, 1);
    }
    int ans = sum;
    for (i = 0; i < n; ++i){
      sum += (n - 1 - b[i] * 2);
      ans = min(ans, sum);
    }
    printf("%d\n", ans);
  }

  return 0;
}

  现在写点更新的线段树比以前熟练一点了~接下来练区间更新的。

comments powered by Disqus