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;
}
现在写点更新的线段树比以前熟练一点了~接下来练区间更新的。