2017-10-19数据结构学习

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循环,复杂度大概是 logNN/S+S
为了使复杂度最小 S取 logNN

代码实现:

#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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值