分块思想
序列元素第K大问题
const int maxn=100010;
const int sqrtN=316;//根号100010
int block[sqrN];//block[i]记录了i块存放元素个数
int table[maxn];//table[i]元素i的个数
void peekMedian(int K)
{
int sum=0;
int idx=0;
while(sum+block[idx]<K)
{
sum+=block[idx++];
}
int num=idx*sqrN;
while(sum+table[num]<K)
{
sum+=table[num++];
}
printf("%d",num);
}
树状数组BIT
lowbit函数
lowbit(x)代表能整除x的最大二次幂
int lowbit(int x)
{
return x & -x;
}
给出一个整数序列A,元素个数为N,接下来的K次对整个数组进行查询或插入操作,每次给出一个查询的数x,要求能实时查询前x个数的和。
const int maxv=10001;
int N=0;
int A[maxv]={0};
int C[maxv]={0};
int lowbit(int x)
{
return x & -x;
}
//构造C数组
int creatc()
{
for(int i=0;i<N;i++)
{
for(int j=i-lowbit(i)+1;j<=i;j++)
{
C[i]+=A[j];
}
}
}
//getSum函数返回前x个整数之和
int grtSum(int x)
{
int sum=0;
for(int i=x;i>0;i-=lowbit(i))
{
sum+=c[i];
}
return sum;
}
//update函数将第x个整数加上v
void update(int x,int v)
{
for(int i=x;i<=N;i++)
{
C[i]+=v;
}
}
若序列为二维的,则将代码修改为:
//getSum函数返回前x个整数之和
int getSum(int x,int y)
{
int sum=0;
for(int i=x;i>0;i-=lowbit(i))
{
for(int j=y;j>0;j-=lowbit(j))
{
sum+=C[i][j];
}
}
return sum;
}
//update函数将第x个整数加上v
void update(int x,int y,int v)
{
for(int i=x;i<=N;i++)
{
for(int j=y;j<maxn;j++)
{
C[i][j]+=v;
}
}
}
如果想求A[a][b]~A[x][y]这个子矩阵元素的和,只需要计算getSum(x,y)-getSum(x-1,y)-getSum(x,y-1)+getSum(x-1,y-1)
如果是区间更新,就是将A[1]到A[x]上的每一个数字都加v,则将getSum和update函数换成如下形式
//getSum函数返回前x个整数之和
int getSum(int x)
{
int sum=0;
for(int i=x;i>0;i-=lowbit(i))
{
sum+=C[i];
}
return sum;
}
//update函数将第x个整数加上v
void update(int x,int v)
{
for(int i=x;i<=N;i++)
{
C[i][j]+=v;
}
}
如果是要让A[x]~A[y]的每个数加上v,则执行
update(y,v);
update(x-1,-v);
经典例题:
给定一个有N个正整数的序列A,对序列中的每个数,求出序列中他左边比它小的数的个数。
#include <iostream>
#include <cstdio>
using namespace std;
const int maxv=10001;
int N=0;
int A[maxv]={0};
int C[maxv]={0};
int lowbit(int x)
{
return x & -x;
}
//getSum函数返回前x个整数之和
int getSum(int x)
{
int sum=0;
for(int i=x;i>0;i-=lowbit(i))
{
sum+=C[i];
}
return sum;
}
//update函数将第x个整数加上v
void update(int x,int v)
{
for(int i=x;i<=N;i++)
{
C[i]+=v;
}
}
int main() {
scanf("%d",&N);
int x;
for(int i=0;i<N;i++)
{
scanf("%d",&x);
update(x,1);
printf("%d\n",getSum(x-1));
}
return 0;
}
用树状数组代替了哈希表,简化查找过程
若需要统计元素左边比它大的数的个数,只需要将最后输出语句改为
printf("%d\n",getSum(N)-getSum(A[i]));//这里的A[i]就是x
离散化思想
若数组中的元素值过大,可以用其他较小的值来替换他,只用表示出元素与元素之间的大小关系即可,例如[520,99999,18,666,8888]等价于[2,5,1,3,4]
#include <iostream>
#include <cstdio>
using namespace std;
const int maxv=10001;
int N=0;
int A[maxv]={0};
int C[maxv]={0};
struct Node{
int val;
int pos;
}temp[maxn];
int lowbit(int x)
{
return x & -x;
}
//getSum函数返回前x个整数之和
int getSum(int x)
{
int sum=0;
for(int i=x;i>0;i-=lowbit(i))
{
sum+=C[i];
}
return sum;
}
//update函数将第x个整数加上v
void update(int x,int v)
{
for(int i=x;i<=N;i++)
{
C[i]+=v;
}
}
bool cmp(Node a,Node b)
{
return a.val<b.val;
}
int main() {
scanf("%d",&N);
int x;
for(int i=0;i<N;i++)
{
scanf("%d",&temp[i].val);
temp[i].pos=i;
}
sort(temp,temp+n,cmp);
for(int i=0;i<n;i++)
{
if(i==0||temp[i].val!=temp[i-1].val)
{
A[temp[i].pos]=i+1;
}
else
{
A[temp[i].pos]=A[temp[i-1].pos];
}
}
for(int i=0;i<n;i++)
{
update(A[i],1);
printf("%d\n",getSum(A[i]-1));
}
return 0;
}
利用树状数组求解第K大问题
//getSum函数返回前x个整数之和
int getSum(int x)
{
int sum=0;
for(int i=x;i>0;i-=lowbit(i))
{
sum+=C[i];
}
return sum;
}
//求元素序列第K大问题(这里假设序列长度[1,maxn])
int findKthElement(int K)
{
int l=1,r=maxn,mid;
while(l<r)
{
mid=(l+r)/2;
if(getSum(mid)>=K)
{
r=mid;
}
}
return l;
}