BIT求LCA
#include<cstdio>
#include<algorithm>
using namespace std;
#define M 1005
int n;
int B[M],A[M],C[M];
int query(int x){
int ans=0;
while(x){
ans=max(ans,B[x]);
x-=x&(-x);
}
return ans;
}
void update(int x,int a){
while(x<=n){
B[x]=max(B[x],a);
x+=x&(-x);
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&A[i]);
C[i]=A[i];
}
sort(C+1,C+n+1);
int len=unique(C+1,C+n+1)-C-1;
for(int i=1;i<=n;i++){
int x=lower_bound(C+1,C+len+1,A[i])-C;
int res=query(x-1);
update(x,res+1);
}
printf("%d\n",query(n));
return 0;
}
分块+二分 求区间第K大值
对于第k大值,并不知道是什么
但由于满足单调性,可以二分答案,复杂度为
logn
把数列分割成多个块 设块的长度为S
询问时同理在块中二分查找,在块外for循环,复杂度大概是
logN∗N/S+S
为了使复杂度最小 S取
logN∗N−−−−−−−−√
代码实现:
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define M 30005
#define QM 200
int A[M];
int B[QM][M],Len[QM];
int S,T;
int n,m,mx=0;
void init(){
for(int k=1;k<=T;k++){
int len=min(n,S*k);
for(int i=1+S*(k-1);i<=len;i++){
B[k][i-S*(k-1)]=A[i];
}
len=len-S*(k-1);
Len[k]=len;
sort(B[k]+1,B[k]+len+1);
}
}
int query(int l,int r,int res){// r-l+1-ans;
int ans=0;
int L=(l-1)/S+1,R=(r-1)/S+1;
if(L==R)for(int i=l;i<=r;i++){
if(A[i]<res)ans++;
}
else {
for(int i=l;i<=S*L;i++)if(A[i]<res)ans++;
for(int i=(R-1)*S+1;i<=r;i++)if(A[i]<res)ans++;
for(int i=L+1;i<=R-1;i++){
ans+=lower_bound(B[i]+1,B[i]+Len[i]+1,res)-B[i]-1;
}
}
return r-l+1-ans;
}
int main(){
scanf("%d%d",&n,&m);
FOR(i,1,n){
scanf("%d",&A[i]);
if(mx<A[i])mx=A[i];
}
S=sqrt(n*log2(n));
T=(n-1)/S+1;
init();
FOR(i,1,m){
int L,R,k;
scanf("%d%d%d",&L,&R,&k);
int l=1,r=mx,res;
while(l<=r){
int mid=(l+r)>>1;
if(query(L,R,mid)>=k)l=mid+1,res=mid;
else r=mid-1;
}
printf("%d\n",res);
}
return 0;
}
归并树+二分 求区间第K大值
听大佬说一般用不到。。。
建树时顺便归并,复杂度为
NlogN
同样二分在询问一个数在区间内在第几位
在树中询问一个数在第几位使用二分查找复杂度为
logN
所以整个询问的复杂度为
log2N
在加上二分整个题目的复杂度为
Mlog3N+NlogN
而空间复杂度为
NlogN
代码实现:
#include<cstdio>
#include<algorithm>
using namespace std;
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define S 200
#define N 30005
int B[S][N];
int A[N];
struct Tree{int l,r;}tree[N<<2];
void build(int l,int r,int p,int dep){
tree[p].l=l,tree[p].r=r;
if(l==r){
B[dep][l]=A[l];
return;
}
int mid=(l+r)>>1;
build(l,mid,p<<1,dep+1);
build(mid+1,r,p<<1|1,dep+1);
int a1=l,a2=mid+1,cnt=l-1;
while(a1<=mid&&a2<=r){
if(B[dep+1][a1]<B[dep+1][a2])B[dep][++cnt]=B[dep+1][a1],a1++;
else B[dep][++cnt]=B[dep+1][a2],a2++;
}
while(a1<=mid)B[dep][++cnt]=B[dep+1][a1],a1++;
while(a2<=r)B[dep][++cnt]=B[dep+1][a2],a2++;
}
int query(int l,int r,int k,int p,int dep){
if(tree[p].l==l&&tree[p].r==r){
return lower_bound(B[dep]+l,B[dep]+r+1,k)-B[dep]-l;
}
int mid=(tree[p].r+tree[p].l)>>1;
if(mid>=r)return query(l,r,k,p<<1,dep+1);
else if(mid<l)return query(l,r,k,p<<1|1,dep+1);
else return query(l,mid,k,p<<1,dep+1)+query(mid+1,r,k,p<<1|1,dep+1);
}
int main(){
int n,m,mx=0;
scanf("%d%d",&n,&m);
FOR(i,1,n){
scanf("%d",&A[i]);
if(mx<A[i])mx=A[i];
}
build(1,n,1,1);
FOR(i,1,m){
int L,R,k;
scanf("%d%d%d",&L,&R,&k);
int l=1,r=mx,res;
while(l<=r){
int mid=(l+r)>>1;
int d=R-L+1-query(L,R,mid,1,1);
if(d>=k){
l=mid+1;
res=mid;
}
else r=mid-1;
}
printf("%d\n",res);
}
return 0;
}
主席树+二分 求区间第K大值
学了那么久这个才是重头戏
静态主席树的实现:(感觉下面这段话说得很清楚了)
首先静态主席树这个东西其实比较好懂,就是对于每个前缀[1,1],[1,2]….[1,n]都建一颗线段树,将数据离散化后,在线段树中统计区间内所有值出现的次数,每一个线段树都是相同的形态,那么这样我们得到一个很好的性质,这些线段树可以“相减”,假设当前查询的是区间[l,r]内第k大的值,那么我们用前缀[1, r]这个线段树和前缀[1,l-1]这颗线段树通过相减加上二分就可以找到答案。由于相邻两颗线段树最多只有logn个节点不同,我们没有必要完全新建一颗线段树,只需要把相同的结点用指针指一下就行,然后新建logn个结点,这样一来时空复杂度为n*logn。
实现 我好意思说自己是copy的吗???
#include<cstdio>
#include<algorithm>
using namespace std;
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define M 30005
#define S 20
int lson[M*S],rson[M*S],Sum[M*S],tot,tree[M];
int A[M],B[M];
void build(int l,int r,int &tid){
tid=++tot;
Sum[tid]=0;
if(l==r)return;
int mid=(l+r)>>1;
build(l,mid,tid);
build(mid+1,r,tid);
}
void insert(int l,int r,int x,int ot,int &tid){
tid=++tot;
lson[tid]=lson[ot];
rson[tid]=rson[ot];
Sum[tid]=Sum[ot]+1;
if(l==r)return;
int mid=(l+r)>>1;
if(x<=mid)insert(l,mid,x,lson[ot],lson[tid]);
else insert(mid+1,r,x,rson[ot],rson[tid]);
}
int query(int lt,int rt,int L,int R,int k){
if(L==R)return L;
int mid=(L+R)>>1;
int cnt=Sum[lson[rt]]-Sum[lson[lt]];
if(cnt>=k)return query(lson[lt],lson[rt],L,mid,k);
else return query(rson[lt],rson[rt],mid+1,R,k-cnt);
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
FOR(i,1,n)scanf("%d",&A[i]),B[i]=A[i];
sort(B+1,B+1+n);
int len=unique(B+1,B+1+n)-B-1;
build(1,len,tree[0]);
FOR(i,1,n){
int x=lower_bound(B+1,B+len+1,A[i])-B;
insert(1,len,x,tree[i-1],tree[i]);
}
FOR(i,1,m){
int l,r,k;
scanf("%d%d%d",&l,&r,&k);
printf("%d\n",B[query(tree[l-1],tree[r],1,len,r-l+2-k)]);
}
return 0;
}