Different GCD Subarray Query HDU - 5869(树状数组,离线)

题目链接:HDU-5869
题意:一组序列,多次询问,询问区间 [ l , r ] [l,r] [l,r]gcd不同的子区间有多少个。

通过预处理,将问题转化为二维数点问题。
预处理所有以i为右端点的不同gcd的值以及开头位置。
用vector > v[maxn]来存,并且其值是非严格递减的,位置是递减的,并且如果一个gcd出现多次只储存靠后位置的。

然后就是套路了,按查询右端点排序,二维数点那样做就可以了。

#include
using namespace std;

const int maxn=1e5+7;
const int maxm=1e6+7;

vector<pair<int,int> > v[maxn];

struct Node{
    int l,r,id;
    bool operator <(const Node& x)const{
        return r<x.r;
    }
}a[maxn];

int sum[maxn];
int n;
void add(int x,int v){
    for(;x<=n;x+=x&-x) sum[x]+=v;
}

int ask(int x){
    int res=0;
    for(;x;x-=x&-x) res+=sum[x];
    return res;
}
int vis[maxm];
int res[maxn];
int main(){
    int q,x;
    while(scanf("%d%d",&n,&q)!=EOF){
        for(int i=1;i<=n;++i){
            scanf("%d",&x);
            int last=x,pos=i;
            v[i].push_back(make_pair(x,i));
            for(int j=0;j<v[i-1].size();++j){
                int val=__gcd(x,v[i-1][j].first);
                int pp=v[i-1][j].second;
                if(val!=last){
                    v[i].push_back({val,pp});
                    last=val;
                }
            }
        }

        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<=q;++i){
            while(j<=a[i].r){
                for(int k=0;k<v[j].size();++k){
                    int val=v[j][k].first;
                    if(vis[val]) add(vis[val],-1);
                    add(v[j][k].second,1);
                    vis[val]=v[j][k].second;
                }
                ++j;
            }
            res[a[i].id]=ask(a[i].r)-ask(a[i].l-1);
        }
        for(int i=1;i<=q;++i) printf("%d\n",res[i]);


        for(int i=1;i<=n;++i){
            sum[i]=0;
            for(int j=0;j<v[i].size();++j) vis[v[i][j].first]=0;
            v[i].clear();
        }
    }

    return 0;
}

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