树状数组入门—逆序对统计
2015-10-23 09:29
405 查看
看了各位大神的博客,自己太弱,引用了一些,望见谅
树状数组:
一、树状数组概念:
![](http://img.blog.csdn.net/20151023151532863?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
![](file:///C:/Documents%20and%20Settings/Stu/Local%20Settings/Application%20Data/YNote/data/z472421519@163.com/fba877f3b8304940aa05156a650f3b81/20130820135411406.png)
树状数组是一种修改和查询复杂度都是O(logn)的数据结构,如图所示,
定义如下:
c[1]=a[1]
c[2]=a[1]+a[2]
c[3]=a[3]
c[4]=a[1]+a[2]+a[3]+a[4]
....
分析上面的几组式子可知,当 i 为奇数时,c[1]=a[i] ;
当 i 为偶数时,就要看 i 的因子中最多有二的多少次幂,例如,
6 的因子中有 2 的一次幂,等于 2 ,所以 c[6]=a[5]+a[6](由六向前数两个数的和);
4 的因子中有 2 的两次幂,等于 4 ,所以 c[4]=a[1]+a[2]+a[3]+a[4](由四向前数四个数的和)。
定义lowbit(i)为i含有的最大的2的幂形式的因子
计算方法为 lowbit(i)=i&(-i),将他表示为二进制的形式,lowbit(i)=i的二进制最小位的权值
如:6=110,最小位为2,4=100最小位为4
因为负数的补码是在其反码的末位加1,所以lowbit(x)=x&(-x);
二基本操作:
1、求数组的和:
1)令sum=0,
2)如果n>0,令sum+=c
,进行第三步
3)n-=lowbit(n)(删掉n的最小位),进行第二步
2、修改(update)—给某个节点加上x:
1)当i<=n时,执行下一步,否则算法结束;
2)c[i]+=x;i+=lowbit(i)(使i的最小位乘二),返回第一步
三、求:
1)记录数列的相对位置:
2)rank[现在处于的位置]=应该的位置(从小到大排序的位置)—(离散化处理)
3)i从1开始循环到n:分别update(rank[i])
4)ans+=i-sum(rank[i])(统计比它小的数的个数,i为已插入数字的总个数,相减得到由i产生的逆序对的个数);
A—NOIP2013火柴排队:
题意:
给定两个长度相等的火柴队列,队列中任意两个相邻的数可以交换,记d(i)=(ai-bi)^2;
求使得sig(d(i))最小需要交换多少次;
分析:最小的状态为a中第i小的数对应b中第i小的数;
保持其中一个队列不变移动另一个就可(移动任意一个都是等价的)。于是问题等同为寻找
a队中逆序对的个数
题解:寻找逆序对:
1)归并排序寻找逆序对,
在每次merge时记录一下交换的次数
2)树状数组数组查找逆序对
3)寻找拐点(烜犇神作)。。。
树状数组代码:
B—luogu2448无尽的生命:
题意:
逝者如斯夫,不舍昼夜!
叶良辰认为,他的寿命是无限长的,而且每天都会进步。
叶良辰的生命的第一天,他有1点能力值。第二天,有2点。第n天,就有n点。也就是S[i]=i
但是调皮的小A使用时光机,告诉他第x天和第y天,就可以任意交换某两天的能力值。即S[x]<-->S[y]
小A玩啊玩,终于玩腻了。
叶良辰:小A你给我等着,我有100种办法让你生不如死。除非能在1秒钟之内告知有多少对“异常对”。也就是说,最后的能力值序列,有多少对的两天x,y,其中x<y,但是能力值S[x]>S[y]?
小A:我好怕怕啊。
于是找到了你。
题解:裸逆序对+离散化+区间处理
分析:
1、树状数组求逆序对
2、map映射离散化
3、移动的第i个元素产生的逆序对=abs(第i个元素的权值-第i个元素的位置-1)-树状数组统计过的位于该区间的逆序对个数
C-poj2299
题意:给定n个数,求逆序对个数
题解:裸逆序对(树状数组)+离散化(比上题简单)
代码:
树状数组:
一、树状数组概念:
![](file:///C:/Documents%20and%20Settings/Stu/Local%20Settings/Application%20Data/YNote/data/z472421519@163.com/fba877f3b8304940aa05156a650f3b81/20130820135411406.png)
树状数组是一种修改和查询复杂度都是O(logn)的数据结构,如图所示,
定义如下:
c[1]=a[1]
c[2]=a[1]+a[2]
c[3]=a[3]
c[4]=a[1]+a[2]+a[3]+a[4]
....
分析上面的几组式子可知,当 i 为奇数时,c[1]=a[i] ;
当 i 为偶数时,就要看 i 的因子中最多有二的多少次幂,例如,
6 的因子中有 2 的一次幂,等于 2 ,所以 c[6]=a[5]+a[6](由六向前数两个数的和);
4 的因子中有 2 的两次幂,等于 4 ,所以 c[4]=a[1]+a[2]+a[3]+a[4](由四向前数四个数的和)。
定义lowbit(i)为i含有的最大的2的幂形式的因子
计算方法为 lowbit(i)=i&(-i),将他表示为二进制的形式,lowbit(i)=i的二进制最小位的权值
如:6=110,最小位为2,4=100最小位为4
因为负数的补码是在其反码的末位加1,所以lowbit(x)=x&(-x);
二基本操作:
1、求数组的和:
1)令sum=0,
2)如果n>0,令sum+=c
,进行第三步
3)n-=lowbit(n)(删掉n的最小位),进行第二步
2、修改(update)—给某个节点加上x:
1)当i<=n时,执行下一步,否则算法结束;
2)c[i]+=x;i+=lowbit(i)(使i的最小位乘二),返回第一步
三、求:
1)记录数列的相对位置:
2)rank[现在处于的位置]=应该的位置(从小到大排序的位置)—(离散化处理)
3)i从1开始循环到n:分别update(rank[i])
4)ans+=i-sum(rank[i])(统计比它小的数的个数,i为已插入数字的总个数,相减得到由i产生的逆序对的个数);
A—NOIP2013火柴排队:
题意:
给定两个长度相等的火柴队列,队列中任意两个相邻的数可以交换,记d(i)=(ai-bi)^2;
求使得sig(d(i))最小需要交换多少次;
分析:最小的状态为a中第i小的数对应b中第i小的数;
保持其中一个队列不变移动另一个就可(移动任意一个都是等价的)。于是问题等同为寻找
a队中逆序对的个数
题解:寻找逆序对:
1)归并排序寻找逆序对,
在每次merge时记录一下交换的次数
2)树状数组数组查找逆序对
3)寻找拐点(烜犇神作)。。。
树状数组代码:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int MAXN=100005,mod=99999997; int t[MAXN],rank[MAXN],n; struct node { int num,wi; }a[MAXN],b[MAXN]; bool cmp1(const node x,const node y) { return x.wi<y.wi; } inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x*10+ch-'0'); ch=getchar(); } return f*x; } int lowbit(int x) { return x&(-x); } int ask(int x) { int tmp=0; for(int i=x;i;i-=lowbit(i)) tmp+=t[i]; return tmp; } void update(int x) { for(int i=x;i<=n;i+=lowbit(i)) t[i]++; } int main() { n=read(); for(int i=1;i<=n;i++) { a[i].wi=read(); a[i].num=i; } for(int i=1;i<=n;i++) { b[i].wi=read(); b[i].num=i; } sort(a+1,a+n+1,cmp1); sort(b+1,b+n+1,cmp1); for(int i=1;i<=n;i++) rank[a[i].num]=b[i].num; int ans=0; for(int i=1;i<=n;i++) { update(rank[i]); ans=(ans+i-ask(rank[i]))%mod; } printf("%d\n",ans); return 0; }
B—luogu2448无尽的生命:
题意:
逝者如斯夫,不舍昼夜!
叶良辰认为,他的寿命是无限长的,而且每天都会进步。
叶良辰的生命的第一天,他有1点能力值。第二天,有2点。第n天,就有n点。也就是S[i]=i
但是调皮的小A使用时光机,告诉他第x天和第y天,就可以任意交换某两天的能力值。即S[x]<-->S[y]
小A玩啊玩,终于玩腻了。
叶良辰:小A你给我等着,我有100种办法让你生不如死。除非能在1秒钟之内告知有多少对“异常对”。也就是说,最后的能力值序列,有多少对的两天x,y,其中x<y,但是能力值S[x]>S[y]?
小A:我好怕怕啊。
于是找到了你。
题解:裸逆序对+离散化+区间处理
分析:
1、树状数组求逆序对
2、map映射离散化
3、移动的第i个元素产生的逆序对=abs(第i个元素的权值-第i个元素的位置-1)-树状数组统计过的位于该区间的逆序对个数
#include <cstdio> #include <map> #include <algorithm> using namespace std; const int MAXN=200005; int t[MAXN],size,n; map<int ,int > fa; struct node { int wi,num,id; void var(int w,int N) { wi=w; num=N; } }nodes[MAXN]; bool comp1(const node a,const node b) { return a.wi<b.wi; } bool comp2(const node a,const node b) { return a.num<b.num; } void sw(int x,int y) { int f1=nodes[x].wi; int f2=nodes[y].wi; nodes[y].wi=f1; nodes[x].wi=f2; } int lowbit(int x) { return x&(-x); } int ask(int x) { int tmp=0; for(int i=x;i;i-=lowbit(i)) tmp+=t[i]; return tmp; } void update(int x) { for(int i=x;i<=MAXN-1;i+=lowbit(i)) t[i]++; return ; } void init() { sort(nodes+1,nodes+size+1,comp2); for(int i=1;i<=size;i++) { nodes[i].id=i; fa[nodes[i].num]=i; } sort(nodes+1,nodes+size+1,comp1); return ; } void work() { long long int ans=0; for(int i=1;i<=size;i++) { ans+=ask(MAXN-1)-ask(nodes[i].id); update(nodes[i].id); if(nodes[i].wi>nodes[i].num) { int k=(fa[nodes[i].wi]-fa[nodes[i].num]-1); ans+=max(0,nodes[i].wi-nodes[i].num-k-1); } else { int k=(fa[nodes[i].num]-fa[nodes[i].wi]-1); ans+=max(0,nodes[i].num-nodes[i].wi-k-1); } } printf("%lld\n",ans); } int main() { int u,v,f1,f2; scanf("%d",&n); size=0; for(int i=1;i<=n;i++) { scanf("%d%d",&u,&v); if(!fa.count(u)) { nodes[++size].var(u,u); fa[u]=size; f1=size; } else f1=fa[u]; if(!fa.count(v)) { nodes[++size].var(v,v); fa[v]=size; f2=size; } else f2=fa[v]; swap(nodes[f1].num,nodes[f2].num); } init(); work(); return 0; }
C-poj2299
题意:给定n个数,求逆序对个数
题解:裸逆序对(树状数组)+离散化(比上题简单)
代码:
#include <cstdio> #include <cstring> #include <algorithm> #include <map> using namespace std; const int MAXN=500000+5; int t[MAXN],n,rank[MAXN]; struct node { int num,wi; }node1[MAXN]; bool comp1(const node a,const node b) { return a.wi<b.wi; } int lowbit(int x) { return x&(-x); } int ask(int x) { int tmp=0; for(int i=x;i;i-=lowbit(i)) tmp+=t[i]; return tmp; } void update(int x) { for(int i=x;i<=n;i+=lowbit(i)) t[i]++; return ; } int main() { while(1) { scanf("%d",&n); if(!n) break; memset(t,0,sizeof(t)); for(int i=1;i<=n;i++) { scanf("%d",&node1[i].wi); node1[i].num=i; } sort(node1+1,node1+n+1,comp1); for(int i=1;i<=n;i++) rank[node1[i].num]=i; long long int ans=0; for(int i=1;i<=n;i++) { update(rank[i]); ans+=i-ask(rank[i]); } printf("%I64d\n",ans); } return 0; }
相关文章推荐
- 九度OJ 1100:最短路径 (最短路径)
- 关于oracle比较时间的用法
- 完成这个例子,说出java中针对异常的处理机制。
- [OrangePi] Installation on internal EMMC
- 注意细节
- 九度OJ 1100:最短路径 (最短路径)
- 制作ubuntu的U盘启动盘
- 空间直线与平面的交点
- CSS旋转图片
- 使用cardview和recycleview时碰到的一些问题
- 整理iOS9适配中出现的坑(图文)
- PhoneGap+Angularjs+ionic 环境搭建
- LoadRunner编程之跳出迭代
- [OrangePi] Installation on SD Card
- 第七周项目6-停车场模拟
- 第7周SHH数据结构—【项目1 - 建立顺序环形队列算法库 .】
- 第八周项目1-建立顺序串的算法库
- 第7周 项目2-建立链队算法库
- java高精度开平方
- oracle几个服务的作用