HDU1394 Minimum Inversion Number 线段树
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1394
题目大意:定义逆序数, 一个数列前面的数比后面的数大的数对的个数, 给出数列, 求逆序数。
解题思路:我现在学习的是线段树, 所以我用线段树的思想去解决问题。暴力是n^2的复杂度, 但是只要知道了第一个数列的逆序数, 其他的都可以通过用O(1) 的方式得到, 所以关键是求初始序列的逆序数, 暴力方法是n^2, 由于最大不超过n所以问题就转化为了插入一个a[i], 求前i-1个数中a[i]~n的数的个数。这就是线段树的一个查询功能。 问题就转化为O(nlogn)了。
代码:
#include <iostream> #include <cstdio> #include <algorithm> using namespace std; #define lson l, m, rt << 1 #define rson m+1, r, rt << 1 | 1 const int maxn = 5555; int sum[maxn<<2]; void PushUP(int rt) { sum[rt] = sum[rt<<1] + sum[rt<<1|1]; } void build(int l, int r, int rt) { sum[rt] = 0; if( l == r ) return; int m = (l+r) >> 1; build(lson); build(rson); } void update(int p, int l, int r, int rt) { if( l == r ) { sum[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 sum[rt]; } int m = (r+l) >> 1; int ret = 0; if( L <= m ) ret += query(L, R, lson); if( R > m ) ret += query(L, R, rson); return ret; } int x[maxn]; int main() { int n; while( scanf("%d", &n) == 1 ) { build(0, n-1, 1); int sum = 0; for( int i = 0; i < n; i++ ) { scanf( "%d", &x[i]); sum += query(x[i], n-1, 0, n-1, 1); update(x[i], 0, n-1, 1); } int ret = sum; for( int i = 0; i < n; i++ ) { sum += n - x[i] - x[i] - 1; ret = min( ret, sum ); } printf( "%d\n", ret ); } return 0; }
思考: 我接触线段树的时间实在是很短, 所以很多问题联系不起来。线段树为什么要叫线段树呢, 因为他处理的就是区间问题, 所以如果将问题转化为区间查询问题, 就可以用线段树解决问题,当然还有更新节点等一些很麻烦的事情, 所以说还得慢慢做题积累, 一个月后我感觉线段树差不多会入门了。
posted on 2017-06-06 09:56 FriskyPuppy 阅读(181) 评论(0) 编辑 收藏 举报