题意:
给出 n n n 个数字,一共 q q q 次查询,每次询问一个 l l l、 r r r,查询区间 [ l , r ] [l,r] [l,r] 中有多少个不同的 g c d gcd gcd,其中一个子区间代表一个 g c d gcd gcd。 ( 1 ≤ n , q ≤ 1 0 5 , 1 ≤ a i ≤ 1 0 6 ) (1\leq n,q\leq 10^5,1\leq a_i\leq 10^6) (1≤n,q≤105,1≤ai≤106)
思路:
区间查询不同 g c d gcd gcd 的个数,这类题像一类套路问题,主要要抓住 g c d gcd gcd 的几个性质。
- 固定右端点,移动左端点, g c d gcd gcd 的值从 a r a_r ar 不断变化,要么不变,要么至少除 2 2 2(因为 g c d gcd gcd 最小值为 2 2 2)。因此固定右端点之后,只会存在至多 l o g log log 个不同的 g c d gcd gcd,我们对于相同的 g c d gcd gcd 仅保留最靠右的位置。
- 因此对于每个右端点,我们记录一个 v e c t o r vector vector,存储对于这个右端点的所有不同的 g c d gcd gcd 值。我们可以根据 v e c t o r [ i − 1 ] vector[i-1] vector[i−1] 来更新 v e c t o r [ i ] vector[i] vector[i]。
处理完上述操作之后,我们得到了 n ∗ l o g n n*logn n∗logn 个 ( l , r , g c d ) (l,r,gcd) (l,r,gcd) 三元组,然后我们将所有查询按照右端点排序。
记录一个 p o s pos pos,不断右移到查询的右端点位置,每次移动时将 v e c t o r [ p o s ] vector[pos] vector[pos] 内的信息存储到树状数组中,即对于每个三元组 ( l , p o s , g c d ) (l,pos,gcd) (l,pos,gcd),如果该 g c d gcd gcd 未出现过,则在树状数组的 l l l 位置 + 1 +1 +1,并设置 v i s [ g c d ] = l vis[gcd]=l vis[gcd]=l。如果该 g c d gcd gcd 出现过,则比较 l l l 是否比 v i s [ g c d ] vis[gcd] vis[gcd] 更大,如果更大,则修改 g c d gcd gcd 在树状数组中的位置。上述操作即不断维护 g c d gcd gcd 最靠右的出现位置。然后对于每个查询,直接在树状数组中区间查询即可。
总结:
此题最关键的在于发现区间 g c d gcd gcd 不断除 2 2 2 的性质,然后将查询离线利用树状数组不断维护每个 g c d gcd gcd 最后出现的位置即可完成。
代码:
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof a);
#define rep(i,a,b) for(int i = a; i <= b; i++)
#define per(i,a,b) for(int i = a; i >= b; i--)
#define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
typedef long long ll;
typedef double db;
const int N = 1e6+100;
const db EPS = 1e-9;
using namespace std;
void dbg() {cout << "\n";}
template<typename T, typename... A> void dbg(T a, A... x) {cout << a << ' '; dbg(x...);}
#define logs(x...) {cout << #x << " -> "; dbg(x);}
int n,q,a[N],vis[N];
ll ans[N],c[N];
struct Node{
int l,r,id;
bool operator < (Node xx) const {
return r < xx.r;
}
}Q[N];
vector<pair<int,int> > base[N];
inline int lowbit(int x) {return x&(~x+1);}
inline void update(int x,ll v) {for(;x<=n;x+=lowbit(x)) c[x] += v;}
inline ll ask(int x){
ll tp = 0;
while(x) tp += c[x], x -= lowbit(x);
return tp;
}
int gcd(int a,int b){
return b == 0 ? a:gcd(b,a%b);
}
int main()
{
while(~scanf("%d%d",&n,&q)){
rep(i,1,n) scanf("%d",&a[i]);
rep(i,1,n) base[i].clear();
rep(i,0,n) c[i] = 0;
//更新每个点的vector
rep(i,1,n){
base[i].push_back(make_pair(i,a[i])); vis[a[i]] = 1;
for(auto &v:base[i-1]){
int tp = gcd(v.second,a[i]);
if(!vis[tp]){
base[i].push_back(make_pair(v.first,tp));
vis[tp] = 1;
}
}
for(auto &v:base[i])
vis[v.second] = 0;
}
rep(i,1,q) scanf("%d%d",&Q[i].l,&Q[i].r), Q[i].id = i;
sort(Q+1,Q+1+q);
int pos = 0;
rep(i,1,q){
while(pos <= Q[i].r){
for(auto &v:base[pos]){
int hp = v.first;
if(hp > vis[v.second]){
if(vis[v.second]) update(vis[v.second],-1);
vis[v.second] = hp;
update(vis[v.second],1);
}
}
pos++;
}
ans[Q[i].id] = ask(Q[i].r)-ask(Q[i].l-1);
}
rep(i,1,q) printf("%lld\n",ans[i]);
memset(vis,0,sizeof vis);
}
return 0;
}