题干
链接:走你
题解
/**
*@author micsay
*先用RMQ预处理各个区间的gcd,然后再用二分法+map统计相同gcd区间的区间个数
*具体解题思路请看注释
**/
#include<cstdio>
#include<cmath>
#include<cstring>
#include<map>
//构建mp数组的函数
int _func(int a,int b){
if(!a)return b;
return _func(b%a,a);
}
typedef long long T;
typedef long long ll;
const int MAX = 100010;//原始数组大小
int root_num;//原始数组真正大小
T root[MAX];
T mp[MAX][65];//1<<65为最大可以保存的64bit整数,mp保存RMQ预处理数据
void RMQ_init(){
memset(mp,0,sizeof(mp));
int lg2 = log(root_num)/log(2);
for(int i=1;i<=root_num;i++)mp[i][0]=root[i];//将root数据导入mp(初始化)
for(int j=1;j<=lg2;j++){
for(int i=1;i<=root_num;i++){
//构建mp[][]数组
//分为:区间一[i,i+(1<<(j-1))-1]和区间二[i+(1<<(j-1)),i+(1<<j)-1]两部分
//其中,区间[i,i+(1<<j)-1]的大小为1<<j,所以正好每个区间大小为1<<(j-1)
mp[i][j]=mp[i][j-1];
if(i+(1<<(j-1))<=root_num)
mp[i][j]=_func(mp[i][j-1],mp[i+(1<<(j-1))][j-1]);
}
}
}
T query(int l,int r){
int lg2 = log(r-l+1)/log(2);
//区间[l,r]大小为(r-l+1),可以分成两个区间,分别为:区间一[l,l+(1<<lg2)-1],
//区间二[r-(1<<lg2)+1,log2](注意,两个区间会有交集,但是并不影响结果)
return _func(mp[l][lg2],mp[r-(1<<lg2)+1][lg2]);
}
//用map记录
std::map<int,ll>V;
bool Same(const int l,int r,const T gcd){
//看区间[l,r]的gcd是否与gcd相同
return gcd == query(l,r);
}
//用二分法
//因为区间[i,k](i<=k<=root_num)的gcd随着k变大而呈递减状态
//所以比如区间[1,mid](mid=(1+root_num))的gcd和区间[1,root_num]的gcd相同,那么,
//就不要再遍历区间[1,p]了(mid<=p<root_num),而是直接使V[gcd]+=(root_num-mid+1)
//然后再遍历[1,mid-1]
void find(const int l,int mid,int r,const T p_gcd){
if(l>r)return;
if(Same(l,mid,p_gcd)){
if(V.find(p_gcd)==V.end()){
V[p_gcd] = ll(r-mid+1);
}else{
V[p_gcd] += ll(r-mid+1);
}
r=mid-1;
if(l<=r)
find(l,(l+r)/2,r,query(l,r));
return;
}else{
mid = mid + (r-mid)/2 + (r-mid)%2;
find(l,mid,r,p_gcd);
}
}
int main(){
int t,q;
scanf("%d",&t);
for(int cas=1;cas<=t;cas++){
scanf("%d",&root_num);
for(int i=1;i<=root_num;i++){
scanf("%I64d",&root[i]);
}
RMQ_init();
V.clear();
//构建V
for(int i=1;i<=root_num;i++){
find(i,(i+root_num)/2,root_num,query(i,root_num));
}
scanf("%d",&q);
printf("Case #%d:\n",cas);
int l,r;
while(q--){
scanf("%d%d",&l,&r);
ll ans = query(l,r);
printf("%I64d %I64d\n",ans,V[ans]);
}
}
return 0;
}