首页 > 代码库 > 线段树求逆序数方法 HDU1394&&POJ2299
线段树求逆序数方法 HDU1394&&POJ2299
为什么线段树可以求逆序数?
首先要求一个逆序数有两种方式:可以从头开始往后找比当前元素小的值,也可以从后往前找比当前元素大的值,有几个逆序数就是几。
线段树就是应用从后往前找较大值得个数。(一边更新一边查)
当前个数是 n = 10
元素 9 5 3
9先加入线段树,T【9】+=1;查从T【9】到T【10】比9大的值,没有sum = 0;
5 加入线段树,T【5】 += 1,查从T【5】到T【10】比5大的值,有一个9,sum +=1;
3 加入线段树,T【3】 += 1,查从T【3】到T【10】比3大的值,有两个9和5,sum +=2;
最终sum = 3;
若元素值不能确定,那么首先讲数列离散化,在从前往后枚举,找后面比前面小的统计个数……
离散化:例如2 5 8 3 10 等价于 1 3 4 2 5,可以通过排序加小小处理解决。
例如:
元素 9 5 3 4 1 离散化后得到索引
索引 1 2 3 4 5
将元素升序排序
元素 1 3 4 5 9
索引 5 3 4 2 1
枚举到第i个数,我们需要求出从1到i-1中有多少个比a[i]大的数,更新答案。
具体怎么做呢?
每次枚举完一个数之后,将这个数插入到线段树里(注意:插到与其对应的位置)。
举个例子:3 2 4 1。则线段树的变化应该为:t[3]+=1;t[2]+=1;t[4]+=1;t[1]+=1;
设x=a[i],这样,在插入一个数X时,首先求一下t[x+1]~t[n]的和,这个和就是1~i-1中有多少个比a[i]大的数
因为求一个数列的逆序数可以从两个
在你插a[i]时,前面已经有多少个比a[i]大的数
POJ 2299 #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> #include <cmath> using namespace std; #define MAX INT_MAX #define MIN INT_MIN #define LL __int64 #define init(a) memset(a,0,sizeof(a)) #define lson l , m , rt << 1 #define rson m + 1 , r , rt << 1 | 1 const int maxn = 500010; using namespace std; int T[maxn<<2], index[maxn], num[maxn]; int n; int cmp(const int i, const int j) { return num[i] < num[j]; } void update( int l, int r,int rt, int num, int add) { T[rt] += add; if(r==l) return ; int m = (l+r) >> 1; if(num <= m) update(lson, num, add); else update(rson, num, add); } int query( int l, int r, int rt,int L, int R) { if(L<=l && r<=R) return T[rt]; int m = (l+r) >> 1; int ans = 0; if(L <= m) ans += query(lson, L, R); if(R > m) ans += query(rson, L, R); return ans; } int main() { while(~scanf("%d", &n)) { if(n==0) break; init(index); for(int i=1;i<=n;i++) { scanf("%d", &num[i]); update(1, n,1, i, 1);//离散化 index[i] = i; } sort(index+1, index+n+1,cmp); //排序num,更新索引 LL sum = 0; for(int i=1;i<=n;i++) { sum += (query( 1, n,1, 1, index[i])-1); update(1, n,1, index[i], -1); } printf("%I64d\n", sum); } return 0; }
HDU 1394
题意:一组数 每次把最前面的元素放在最后生成新的序列,再统计逆序数,求所有逆序中最小值
有一个公式:先求出原数列的逆序数,sum
那么每次新逆序数 = sum-a[i] + (n-a[i] - 1)
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> #include <cmath> using namespace std; #define MAX INT_MAX #define MIN INT_MIN #define LL __int64 #define init(a) memset(a,0,sizeof(a)) #define lson l , m , rt << 1 #define rson m + 1 , r , rt << 1 | 1 const int maxn = 500010; using namespace std; int T[maxn<<2], index[maxn], num[maxn]; int n; int cmp(const int i, const int j) { return num[i] < num[j]; } void update( int l, int r,int rt, int num, int add) { T[rt] += add; if(r==l) return ; int m = (l+r) >> 1; if(num <= m) update(lson, num, add); else update(rson, num, add); } int query( int l, int r, int rt,int L, int R) { if(L<=l && r<=R) return T[rt]; int m = (l+r) >> 1; int ans = 0; if(L <= m) ans += query(lson, L, R); if(R > m) ans += query(rson, L, R); return ans; } int main() { while(~scanf("%d", &n)) { if(n==0) break; init(index); for(int i=1;i<=n;i++) { scanf("%d", &num[i]); update(1, n,1, i, 1); index[i] = i; } sort(index+1, index+n+1,cmp); LL sum = 0; for(int i=1;i<=n;i++) { sum += (query( 1, n,1, 1, index[i])-1); update(1, n,1, index[i], -1); } // printf("%I64d\n", sum); LL ans = sum; for(int i = 1;i<=n;i++) { sum = sum - num[i] + (n-num[i]- 1); ans = min(sum,ans); } cout<<ans<<endl; } return 0; }
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。