Codeforces474 F. Ant colony(线段树+二分)

题意:

给定长度为n的序列a,q次询问
每次询问给定[L,R],问[L,R]内不能整除区间内所有数的数的个数有多少个

数据范围:n,q<=1e5,a(i)<=1e9

解法:

将问题转化为求区间内可以整除其他所有数的数的个数,用区间长度减去就是答案,
那么问题变为求区间gcd在区间内的个数

区间gcd可以用线段树求

求个数:
先预处理,将值等于x的所有下标放在一个容器里,预处理完每种x的位置都在一个容器中,
计算区间内x有多少个的时候,直接在x所在容器二分左右端点在容器中的位置,计算两个位置中有多少个数即可。
这题a(i)的数据范围1e9,但是n只有1e5,多打一个离散化就行了。

code:

#include<bits/stdc++.h>
using namespace std;
const int maxm=1e5+5;
vector<int>g[maxm];
int a[maxm<<2];
int b[maxm];
int xx[maxm];
int n,q;
int gcd(int a,int b){
    return b==0?a:gcd(b,a%b);
}
void pushup(int node){
    a[node]=gcd(a[node*2],a[node*2+1]);
}
void build(int l,int r,int node){
    if(l==r){
        a[node]=b[l];
        return ;
    }
    int mid=(l+r)/2;
    build(l,mid,node*2);
    build(mid+1,r,node*2+1);
    pushup(node);
}
int ask(int st,int ed,int l,int r,int node){
    if(st<=l&&ed>=r)return a[node];
    int mid=(l+r)/2;
    int ans=0;
    if(st<=mid)ans=gcd(ans,ask(st,ed,l,mid,node*2));
    if(ed>mid)ans=gcd(ans,ask(st,ed,mid+1,r,node*2+1));
    return ans;
}
signed main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>b[i],xx[i]=b[i];
    }
    build(1,n,1);
    sort(xx+1,xx+1+n);
    int num=unique(xx+1,xx+1+n)-xx-1;
    for(int i=1;i<=n;i++){
        int x=lower_bound(xx+1,xx+1+num,b[i])-xx;
        g[x].push_back(i);
    }
    cin>>q;
    while(q--){
        int l,r;cin>>l>>r;
        int temp=ask(l,r,1,n,1);
        int pos=lower_bound(xx+1,xx+1+num,temp)-xx;
        if(xx[pos]!=temp){
            cout<<r-l+1<<endl;
        }else{
            int ans=upper_bound(g[pos].begin(),g[pos].end(),r)-lower_bound(g[pos].begin(),g[pos].end(),l);
            cout<<r-l+1-ans<<endl;
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44178736/article/details/107410049