分块思想
hash数组table[10001] 其中table[x]表示整数x的个数。
统计数组block[]表示第i块中存在的个数
求解第K大的元素是什么:首先,从小到大累加block[i] 直到sum>=k。其次从小到大累加table[i]直到sum>=k。
树状数组
lowbit(i):求解i的最低位的1和它右边的所有0。
树状数组的应用大致分为两个
- 单点更新 区间查询
- 区间更新 单点查询
两种不同的应用应该让树状数组例的C[i]值的含义不同。
C[i] 值得覆盖区间位lowbit(i) 即包括a[i] 在内往前数 lowbit(i)个元素。
树状数组的核心函数为getSum与update
单点更新 区间查询
c[i]含义:覆盖区域数值之和
//getSum 返回前x个整数之和,复杂度O(logN)
int getSum (int x)
{
int sum = 0;
for(int i = x;i > 0;i -= lowbit(i))
{
sum+=c[i];
}
return sum;
}
sum(x,y) = getSum(y) - getSum(x -1)
//update 将第x个数加上v
void update(int x,,int v)
{
for(int i = x; i <= N; i+= lowbit(i)){
c[i] += v;
}
}
// 给定一个有N个整数的序列,对序列中的每个数,求出序列中它左边比它小的数
//存放i的个数
hash[A[i]] ++;
res = hash[1] + hash[2]+ ...hash[A[i] - 1]
#include<cstdio>
#include<cstring>
const int maxn = 100010
//非离散化
#define lowbit(i) ((i)&(-i))
int c[maxn];//树状数组
void update(int x,int v)
{
for(int i = x; i < maxn; i+= lowbit(i))
{
c[i] += v;
}
}
int getSum (int x)
{
int sum = 0;
for(int i = x;i > 0;i -= lowbit(i))
{
sum+=c[i];
}
return sum;
}
int main()
{
int n,x;
scanf("%d",&x);
meset(c,0,sizeof(c));
for(int i = 0; i < n;i ++)
{
scanf("%d",&x);
update(x,1);
printf("%d\n",getSum(x-1));
}
return 0;
}
离散化:缩区间
//离散化
#include<cstdio>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn 100010;
#define lowbit(i) ((i)&(-i))
struct Node{
int val;
int pos;
}temp[maxn];//temp数组临时存放 输入数据
int A[maxn];//离散化后的原始数组
int c[maxn];
void update(int x,int v)
{
for(int i = x; i < maxn; i+= lowbit(i))
{
c[i] += v;
}
}
int getSum (int x)
{
int sum = 0;
for(int i = x;i > 0;i -= lowbit(i))
{
sum+=c[i];
}
return sum;
}
}
//按val从小到大排序
bool cmp(Nade a,Node b)
{
return a.val < b.val;
}
int main()
{
int n;
scanf("%d",&n);
meset(c,0,sizeof(c));
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));//查询当前小于A[i]的数的个数
}
return 0;
}
树状数组适合离线查询
求解第K大的数组元素
每个数组存放本数的个数,和分块思想的意义一样
//寻找第一个满足条件的getSum(i)>=K
int findKthElement(int K)
{
int l = 1,r = MAXN,mid;//初始区间[1,MAX]
while(l < r)
{
mid = (l + r)/2;
if(getSum(mid)>=K) r = mid;
else l = mid + 1;
}
return l;
}
//二维树状数组
int c[maxn][maxn];
void update(int x ,inty,int v)
{
for(int i = x; i < maxn ; j += lowbit(i))
{
for(int j = y; j <maxn; j += lowbit(j))
{
c[i][j] += v;
}
}
}
//二维getSum函数返回1,1 到 x,y 的子矩阵元素之和
int getSum(int x,int y)
{
int xum = 0;
for(int i = x; i > 0 ; j -= lowbit(i))
{
for(int j = y; j > 0;j -= lowbit(j))
{
c[i][j] += v;
}
}
return sum;
}
区间更新 单点查询
C[i]含义:覆盖区间被加了多少
int getSum(int x)
{
int sum = 0;
for(int i = x ; i < maxn; i +=lowbit(i))
{
sum+= c[i];
}
return sum;
void update(int x,int v)
{
for(int i = x; i > 0;i -= lowbit(i))
{
c[i] += v;
}
}