您的位置:首页 > 其它

树状数组入门—逆序对统计

2015-10-23 09:29 405 查看
看了各位大神的博客,自己太弱,引用了一些,望见谅
树状数组:
一、树状数组概念:





树状数组是一种修改和查询复杂度都是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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: