这篇博客主要列举一些树状数组的用处
树状数组:区间更新,单点求值。
#include <bits/stdc++.h>
#define L(x) (x<<1)
#define R(x) (x<<1|1)
#define fori(a,b,c) for(int a=b;a<=c;a++)
#define ford(a,b,c) for(int a=c;a>=b;a--)
#define mem0(x) memset(x,0,sizeof(x))
#define mem1(x) memset(x,-1,sizeof(x))
using namespace std;
const int M=1e5+100;
int a[M],n;
int lowbit(int x)
{
return x&(-x);
}
void update(int x,int v)
{
while(x<M)
{
a[x]+=v;
x+=lowbit(x);
}
}
int getsum(int x)
{
int ans=0;
while(x>0)//此处的条件不能是x>=0,因为lowbit(0)=0则会进入死循环
{
ans+=a[x];
x-=lowbit(x);
}
return ans;
}
int main()
{
//freopen("in.txt","r",stdin);
while(scanf("%d",&n)==1&&n)
{
int x,y;
memset(a,0,sizeof(a));
for(int i=0;i<n;i++)
{
scanf("%d%d",&x,&y);
update(x,1);
update(y+1,-1);
}
for(int i=1;i<=n;i++)
{
int tem=getsum(i);
i==n?printf("%d\n",tem):printf("%d ",tem);
}
}
return 0;
}
树状数组二分枚举
题目1:KiKi's K-Number
#include <cstdio>
#include<cstring>
#define L(x) (x<<1)
#define R(x) (x<<1|1)
#define fori(a,b,c) for(int a=b;a<=c;a++)
#define ford(a,b,c) for(int a=c;a>=b;a--)
#define mem0(x) memset(x,0,sizeof(x))
#define mem1(x) memset(x,-1,sizeof(x))
using namespace std;
const int M=1e5+100;
int a[M],n;
int lowbit(int x)
{
return x&(-x);
}
void update(int x,int v)
{
while(x<M)
{
a[x]+=v;
x+=lowbit(x);
}
}
int getsum(int x)
{
int ans=0;
while(x>0)
{
ans+=a[x];
x-=lowbit(x);
}
return ans;
}
int fd(int x,int k)
{
int l=x+1,r=M,m,s=getsum(x);
while(l<r)
{
m=(l+r)/2;
if(getsum(m)-s>=k)r=m;
else l=m+1;
}
return r;
}
int main()
{
//freopen("in.txt","r",stdin);
while(scanf("%d",&n)==1)
{
int x,y,z;
memset(a,0,sizeof(a));
for(int i=0; i<n; i++)
{
scanf("%d",&x);
if(x==0)
{
scanf("%d",&y);
update(y,1);
}
else if(x==1)
{
scanf("%d",&y);
if(getsum(y)-getsum(y-1)>0)update(y,-1);
else printf("No Elment!\n");
}
else if(x==2)
{
scanf("%d%d",&y,&z);
int ans=fd(y,z);
if(ans==M)
printf("Not Find!\n");
else printf("%d\n",ans);
}
}
}
return 0;
}
题目2:P1168 中位数
思路1:假设数字序列的长度是len,那么求中位数就是求其中第(len+1)/2大的数(题目中的len都是奇数)。所以这又和题目1类似了。
方法1:(树状数组+二分+离散化)
#include <bits/stdc++.h>
#include<cstring>
#define L(x) (x<<1)
#define R(x) (x<<1|1)
#define fori(a,b,c) for(int a=b;a<=c;a++)
#define ford(a,b,c) for(int a=c;a>=b;a--)
#define mem0(x) memset(x,0,sizeof(x))
#define mem1(x) memset(x,-1,sizeof(x))
using namespace std;
const int M=1e5+100;
int a[M],b[M],c[M],n;
int lowbit(int x)
{
return x&(-x);
}
void update(int x,int v)
{
while(x<M)
{
a[x]+=v;
x+=lowbit(x);
}
}
int getsum(int x)
{
int ans=0;
while(x>0)
{
ans+=a[x];
x-=lowbit(x);
}
return ans;
}
int fd(int k)
{
int l=1,r=M,m;
while(l<r)
{
m=(l+r)/2;
if(getsum(m)>=k)r=m;
else l=m+1;
}
return r;
}
int getx(int x)
{
return lower_bound(c+1,c+n+1,x)-c;
}
int main()
{
//freopen("in.txt","r",stdin);
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",b+i),c[i]=b[i];
sort(c+1,c+n+1);
for(int i=1;i<=n;i++)
{
update(getx(b[i]),1);
if(i%2)
{
printf("%d\n",c[fd((i+1)/2)]);
}
}
return 0;
}
思路2:其实还可以用两个堆来做,用大顶堆保存前面一半,小顶堆保存后面一半,那么中位数就是大顶堆的堆顶元素,那么我们就只要维护这两个堆就可以了。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<queue>
#include<vector>
#define ll long long
#define pi 3.1415926
using namespace std;
priority_queue<int> f;
priority_queue<int,vector<int>,greater<int> > e;
int main()
{
//freopen("hello.txt","r",stdin);
int n,x;
scanf("%d",&n);
for(int i=1; i<=n; i++)
{
scanf("%d",&x);
f.push(x);
if(f.size()>e.size())//维护两个堆中的元素数量
{
e.push(f.top());
f.pop();
}
if(!e.empty()&&f.top()>e.top())//当大顶堆中最大的元素大于小顶堆中最小的元素时要调整
{
int tem=f.top();
f.pop();
f.push(e.top());
e.pop();
e.push(tem);
}
if(i%2)
{
int sf=f.size(),se=e.size();
if(sf==se)printf("%d\n",(f.top()+e.top())/2);
//当两个堆中的元素数量相等的时候中位数是中间两个数的平均值,其实这个题目中不会出现这种情况,因为题目是要我们求i为奇数时的中位数
else if(sf>se)printf("%d\n",f.top());
else if(se>sf)printf("%d\n",e.top());
}
}
return 0;
}