2017-10-19数据结构学习

来源:互联网 发布:工程预算软件手机版 编辑:程序博客网 时间:2024/06/03 15:37

BIT求LCA

#include<cstdio>#include<algorithm>using namespace std;#define M 1005int 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 200int 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 30005int 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 20int 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;}
原创粉丝点击