题目链接:HDU-5869
题意:一组序列,多次询问,询问区间
[
l
,
r
]
[l,r]
[l,r]gcd不同的子区间有多少个。
通过预处理,将问题转化为二维数点问题。
预处理所有以i为右端点的不同gcd的值以及开头位置。
用vector<pair<> > v[maxn]来存,并且其值是非严格递减的,位置是递减的,并且如果一个gcd出现多次只储存靠后位置的。
然后就是套路了,按查询右端点排序,二维数点那样做就可以了。
#include<bits/stdc++.h>
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;
}
本文介绍一种解决区间GCD子区间计数问题的方法,通过预处理将问题转化为二维数点问题,使用vector存储不同GCD值及其位置,实现高效查询。适用于算法竞赛和数据结构学习。
238

被折叠的 条评论
为什么被折叠?



