[CF474F]Ant colony:线段树

一开始还想怎么快速合并两个区间的最大公因数,其实直接辗转相除法就可以,毕竟O(nlog^2n)过1e5也是可以的。
对于查询区间中有几个数的值==最大公约数,直接把每个值存入vector中upper_bound()二分即可。
本次使用了大不同于寻常的代码风格。(我保证这是最后一次了QwQ)
代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <map>
typedef long long LL;
using std::cin;
using std::cout;
using std::endl;
const int MAXN=100005;
int n;LL a[MAXN],b[MAXN],c[MAXN];
std::vector<int> v[MAXN];
std::map<LL,int> f;
LL gcd(LL x,LL y){
    while(y!=0){
        std::swap(x,y);
        y%=x;
    }
    return x;
}
#define mid ((l+r)>>1)
#define lc (o<<1)
#define rc ((o<<1)|1)
namespace seg{
    LL num[MAXN<<2];int ql,qr;
    void build(int o,int l,int r){
        if(l==r){
            num[o]=a[l];
            return;
        }
        build(lc,l,mid);
        build(rc,mid+1,r);
        num[o]=gcd(num[lc],num[rc]);
    }
    LL query(int o,int l,int r){
        if(ql<=l&&r<=qr){
            return num[o];
        }
        LL ans=0;
        if(mid>=ql) ans=gcd(ans,query(lc,l,mid));
        if(mid<qr) ans=gcd(ans,query(rc,mid+1,r));
        return ans;
    }
}
int getans(LL d){
    if(!f.count(d)) return 0;
    d=f[d];
    return std::upper_bound(v[d].begin(),v[d].end(),seg::qr)
        -std::upper_bound(v[d].begin(),v[d].end(),seg::ql-1);
}
int main(){
    std::ios::sync_with_stdio(false);
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        b[i]=a[i];
    }
    std::sort(b+1,b+n+1);
    int siz=std::unique(b+1,b+n+1)-b-1;
    for(int i=1;i<=n;i++){
        c[i]=std::lower_bound(b+1,b+n+1,a[i])-b;
        f[a[i]]=c[i];
        v[c[i]].push_back(i);
    }
    seg::build(1,1,n);
    int m;
    cin>>m;
    while(m--){
        cin>>seg::ql>>seg::qr;
        LL d=seg::query(1,1,n);
        cout<<seg::qr-seg::ql+1-getans(d)<<endl;
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ErkkiErkko/p/9296593.html