HDU 1394 解题报告 线段树 单点更新
2015-07-16 19:37
387 查看
求逆序对常用方法是归并排序,当然线段树也可以解决,但是空间复杂度就会更大。
这题两者都可用。因为只需要第一次求出逆序对数即可,后面的排列都可以推出来。
具体怎么推?
假设 sum 是第一次求出来的逆序对。 第一次排列是 1 3 6 9 0 8 5 7 4 2
那么,第二次就会是
3 6 9 0 8 5 7 4 2 1
1 被放到末尾,自然需要在 sum 上减去包括1的逆序对了。观察一下,1 是在最左边的,在1还在首部时,右边能跟1组成逆序对的就只有0,所以 sum 需要先减掉 1.
第三次 6 9 0 8 5 7 4 2
1 3 。同上, sum 要减掉 2.
回头看 第二次 ,1 被放在尾部后,可能会有新的逆序对产生,所以能跟尾部1组成逆序对的,没有, 所以是 sum + 0。
但是在第三次时候,前面总共有 0, 1, 2 三个可以跟尾部3组成逆序对。 所以这里就需要 sum + 3。
后面同样规律。
代码:
这题两者都可用。因为只需要第一次求出逆序对数即可,后面的排列都可以推出来。
具体怎么推?
假设 sum 是第一次求出来的逆序对。 第一次排列是 1 3 6 9 0 8 5 7 4 2
那么,第二次就会是
3 6 9 0 8 5 7 4 2 1
1 被放到末尾,自然需要在 sum 上减去包括1的逆序对了。观察一下,1 是在最左边的,在1还在首部时,右边能跟1组成逆序对的就只有0,所以 sum 需要先减掉 1.
第三次 6 9 0 8 5 7 4 2
1 3 。同上, sum 要减掉 2.
回头看 第二次 ,1 被放在尾部后,可能会有新的逆序对产生,所以能跟尾部1组成逆序对的,没有, 所以是 sum + 0。
但是在第三次时候,前面总共有 0, 1, 2 三个可以跟尾部3组成逆序对。 所以这里就需要 sum + 3。
后面同样规律。
代码:
#include <cstdio> #define MAXN (5000 + 10) #define lson rt << 1 #define rson rt << 1 | 1 int ns[MAXN << 2]; int nums[MAXN]; int n, input, sum, ans; void build(int rt, int l, int r) { if (l == r) ns[rt] = 0; else { int mid = (l + r) / 2; build(lson, l, mid); build(rson, mid+1, r); ns[rt] = ns[lson] + ns[rson]; } } int query(int rt, int l, int r, int ll, int rr) { if (l == ll && rr == r) return ns[rt]; else { int mid = (l + r) / 2; if (rr <= mid) return query(lson, l, mid, ll, rr); else if (mid < ll) return query(rson, mid+1, r, ll, rr); else { return query(lson, l, mid, ll, mid) + query(rson, mid+1, r, mid+1, rr); } } } void update(int rt, int l, int r, int id) { if (l == id && r == id) ns[rt] = 1; else { int mid = (l + r) / 2; if (id <= mid) update(lson, l, mid, id); else update(rson, mid+1, r, id); ns[rt] = ns[lson] + ns[rson]; } } int main() { //freopen("testdata/1394.txt", "r", stdin); while (scanf("%d", &n) != EOF) { build(1, 0, n); sum = 0; for (int i = 0; i < n; ++i) { scanf("%d", &nums[i]); sum += query(1, 0, n, nums[i]+1, n); update(1, 0, n, nums[i]); } ans = sum; for (int i = 0; i < n-1; ++i) { //sum -= query(1, 0, n, 0, nums[i]-1); //这种方式同样是先减后加 //sum += query(1, 0, n, nums[i]+1, n); //不过需要注意nums[i]-1会等于-1.这时通过对nums[i]+1进行处理,而 n 就需要+1. sum = sum - nums[i] + (n - nums[i] - 1); if (ans > sum) ans = sum; } printf("%d\n", ans); } }
相关文章推荐
- linux常用指令(新手总结)
- mysql创建外键出错(注意数据库表字段排序)
- 【数据库范式】 分析题第一范式
- VS2010调试JM8.6
- 计算4000000000内的最大f(n)=n值---字符串的问题python实现(五岁以下儿童)
- 『Luogu OJ』『C++』Level 1-4 数组
- 【Python 学习手册笔记】元组、文件及其他
- 泛函编程(34)-泛函变量:处理状态转变-ST Monad
- PHP原理之内存管理中难懂的几个点
- 菜鸟的mongoDB学习---(七)MongoDB 备份(mongodump)与恢复(mongorerstore)
- JVM系列文章(五):Javac编译与JIT编译
- MATLAB中各种对话框的设置
- HDU 2844 Coins
- java内部类
- comparator接口与Comparable接口的区别
- Proftpd遇到的坑
- 2015 HUAS Provincial Select Contest #1 B
- Java基础unit2
- windows下利用x264视频编码h264
- 一个理解PHP面向对象编程(OOP)的实例