HDU-4630(离线)

题目链接:HDU-4630
题意:1~n的全排列,多次询问区间内,两个数字gcd的最大值

预处理出一个数字的所有因子,如果一个因子出现两次以上说明可能作为答案。
对询问按照右端点排序。
按位置从小到大枚举,枚举该数字的所有因子,如果之前该因子出现过,则在该因子之前的位置处更新值为该因子。

如果要用线段树维护,那直接做就行。
如果要用树状数组维护,树状数组维护的是具有前缀性质的, [ l , r ] [l,r] [l,r]的最大值显然不能由 [ 1 , l − 1 ] [1,l-1] [1,l1] [ 1 , r ] [1,r] [1,r]得到,那这里用一个比较优秀的想法,因为我们对询问排序了,那按照位置更新,一定到查询的时候,更新过的位置都是<=当前位置的,那么我们可以将树状数组弄成后缀的形式,即可处理区间最值查询问题。一般情况下例如维护区间和,如果将一个位置加的话,是会对其之后的位置造成影响,这里是之前。

线段树

#include
using namespace std;

const int maxn=5e4+7;

vector<int> v[maxn];

int maxx[maxn<<2|1];

void pushup(int k){
    maxx[k]=max(maxx[k<<1],maxx[k<<1|1]);
}

void build(int l,int r,int k){
    maxx[k]=0;
    if(l==r) return ;
    int mid=(l+r)>>1;
    build(l,mid,k<<1);
    build(mid+1,r,k<<1|1);
}

void updata(int l,int r,int k,int id,int val){
    if(l==r){
        maxx[k]=max(maxx[k],val);
        return ;
    }
    int mid=(l+r)>>1;
    if(id<=mid) updata(l,mid,k<<1,id,val);
    else updata(mid+1,r,k<<1|1,id,val);
    pushup(k);
}

int myfind(int l,int r,int k,int L,int R){
    if(l>=L&&r<=R) return maxx[k];
    int mid=(l+r)>>1;
    int res=0;
    if(L<=mid) res=max(res,myfind(l,mid,k<<1,L,R));
    if(R>mid) res=max(res,myfind(mid+1,r,k<<1|1,L,R));
    return res;
}

struct Node{
    int l,r,id;
    bool operator <(const Node& x)const{
        return r<x.r;
    }
}a[maxn];
int b[maxn];
int res[maxn];
int pos[maxn];
int main(){
    for(int i=1;i<=50000;++i)
        for(int j=i;j<=50000;j+=i) v[j].push_back(i);
    int t,n,q,x,y,val;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        build(1,n,1);
        for(int i=1;i<=n;++i){
            scanf("%d",&b[i]);
            //pos[x]=i;
        }
        scanf("%d",&q);
        for(int i=1;i<=q;++i){
            scanf("%d%d",&a[i].l,&a[i].r);
            a[i].id=i;
        }
        sort(a+1,a+1+q);

        for(int i=1,j=1;i<=n;++i){
            for(int k=0;k<v[b[i]].size();++k){
                if(pos[v[b[i]][k]]){
                    //cout<
                    updata(1,n,1,pos[v[b[i]][k]],v[b[i]][k]);
                }
                pos[v[b[i]][k]]=i;
            }
            while(j<=q&&a[j].r==i){
                if(a[j].l==a[j].r) res[a[j].id]=0;
                else res[a[j].id]=myfind(1,n,1,a[j].l,a[j].r);

                ++j;
            }
        }

        for(int i=1;i<=q;++i){
            printf("%d\n",res[i]);
            res[i]=0;
        }

        for(int i=1;i<=n;++i) pos[i]=0;
    }

    return 0;
}

树状数组

#include
using namespace std;

const int maxn=5e4+7;

vector<int> v[maxn];

int maxx[maxn];

void add(int x,int v){
    for(;x;x-=x&-x) maxx[x]=max(maxx[x],v);
}
int n;
int ask(int x){
    int res=0;
    for(;x<=n;x+=x&-x) res=max(res,maxx[x]);
    return res;
}

struct Node{
    int l,r,id;
    bool operator <(const Node& x)const{
        return r<x.r;
    }
}a[maxn];
int b[maxn];
int res[maxn];
int pos[maxn];
int main(){
    for(int i=1;i<=50000;++i)
        for(int j=i;j<=50000;j+=i) v[j].push_back(i);
    int t,q,x,y,val;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        for(int i=1;i<=n;++i){
            scanf("%d",&b[i]);
            maxx[i]=0;
        }

        scanf("%d",&q);
        for(int i=1;i<=q;++i){
            scanf("%d%d",&a[i].l,&a[i].r);
            a[i].id=i;
        }
        sort(a+1,a+1+q);
        for(int i=1,j=1;i<=n;++i){
            for(int k=0;k<v[b[i]].size();++k){
                if(pos[v[b[i]][k]]){
                    add(pos[v[b[i]][k]],v[b[i]][k]);
                }
                pos[v[b[i]][k]]=i;
            }
            while(j<=q&&a[j].r==i){
                if(a[j].l==a[j].r) res[a[j].id]=0;
                else res[a[j].id]=ask(a[j].l);

                ++j;
            }
        }

        for(int i=1;i<=q;++i){
            printf("%d\n",res[i]);
            res[i]=0;
        }

        for(int i=1;i<=n;++i) pos[i]=0;
    }

    return 0;
}

你可能感兴趣的:(线段树,树状数组)