CF475D CGCDSSQ
Problem
给出一个长度为\(n(1<=n<=10^{5})\) 的序列和\(q(1<=q<=3*10^{5})\) 个询问,每个询问输出一行,询问\(gcd(a_l,a_{l+1},...,a_r)=x\) 的\((i,j)\) 的对数。
Solution
分治好题!我永远喜欢分治!
注意到一个事实:一个连续区间的 \(\gcd\) 一定是单调不增的,且 \(\gcd\) 每次变化至少除 \(2\) ,也就是说 \(\gcd\) 的数量是 \(\log\) 级的,好神奇吧!
那就分治!
考虑怎么统计当前区间的答案:
- 取中点 \(mid\)。
- 从中间向两边统计 \([l,mid],[mid+1,r]\) 两个区间的 \(\gcd\) ,注意到这两段都是单调不增且 \(\log\) 级的,那首先把这些答案扔进 \(map\) 里面,最后暴力枚举两边的 \(\gcd\) ,他俩的 \(\gcd\) 出现个数就是 \(cnt_{l} \times cnt_{r}\) 。
- 递归左右区间。
于是你优美的做完了这道题!
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define Rep(i,a,b) for(int i=(a);i<(b);++i)
#define rrep(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
template <typename T>
inline void read(T &x){
x=0;char ch=getchar();bool f=0;
while(ch<'0'||ch>'9'){if(ch=='-')f=1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
if(f)x=-x;
}
template <typename T,typename ...Args>
inline void read(T &tmp,Args &...tmps){read(tmp);read(tmps...);}
typedef long long ll;
const int N = 1e5 + 5;
map<int,ll>ans;
int n,m,a[N];
void solve(int l,int r){
if(l == r){
ans[a[l]]++;
return;
}
int mid = (l + r) >> 1;
solve(l,mid);solve(mid+1,r);
int last = a[mid],now = a[mid],cnt = 0;
vector<pair<int,int>>v[2];
rrep(i,mid,l){
now = __gcd(now,a[i]);
if(now != last){
v[0].push_back({last,cnt});
last = now;cnt = 1;
}
else ++cnt;
}
v[0].push_back({last,cnt});
last = now = a[mid+1],cnt = 0;
rep(i,mid+1,r){
now = __gcd(now,a[i]);
if(now != last){
v[1].push_back({last,cnt});
last = now;cnt = 1;
}
else ++cnt;
}
v[1].push_back({last,cnt});
for(auto t1 : v[0])for(auto t2 : v[1])
ans[__gcd(t1.first,t2.first)] += 1ll * t1.second * t2.second;
}
signed main(){
read(n);
rep(i,1,n)read(a[i]);
solve(1,n);
read(m);
while(m--){
int x;
read(x);
printf("%lld\n",ans[x]);
}
}