点击打开链接
第一问区间gcd好求 难在第二问要求整个序列有多少个区间的gcd与之相等
网上很多用rmq做的 整体复杂度是n*(log(n)^3) 这个做法用线段树就会被卡时间
首先要知道 两个数求gcd 除非两数相等 否则只会越来越小且至少变小2倍 满足一定单调性
先说网上的rmq做法
线性枚举确定一个左端点l 再向右找一个右端点r 使这个区间的gcd等于左端点出的数值 即[l,l] [l,l+1] [l,l+2] ... [l,r] 这些小区间的gcd值都一样 而找右端点可以根据上述的单调性来二分 这是一个logn 区间查询gcd又是一个logn 然后再找下一个这样的区间 又是一个logn
其实二分还有区间查询的logn*logn可以用线段树一次logn的查询解决
每次查询找和当前gcd值不相等的区间的左端点 即分界点 在这个过程中 如果每个在查询范围内的区间是当前gcd的倍数 那分界点肯定不在这个区间 可以递归返回去找更右边的区间了 反之 分界点可定在这个区间内 然后继续递归找左右子区间
可能有人会有疑问 如果固定左端点后 每次移动右端点得到的gcd值都不同 都变成一个新的区间 那不就成o(n)了吗 在上面还说了 两个数求gcd 不相等则至少变小2倍 这样gcd很快就变成 1 了 所以根据这个性质知道 整个序列内最多就有log(n)个区间给你查
#include
using namespace std;
#define ll long long
struct node
{
int l;
int r;
int val;
};
map mp;
node tree[400010];
int num[100010];
int n,q;
int getgcd(int a,int b)
{
int t;
while(b>0)
{
t=b;
b=a%b;
a=t;
}
return a;
}
void pushup(int cur)
{
tree[cur].val=getgcd(tree[2*cur].val,tree[2*cur+1].val);
}
void build(int l,int r,int cur)
{
int m;
tree[cur].l=l;
tree[cur].r=r;
if(l==r)
{
tree[cur].val=num[l];
return;
}
m=(l+r)/2;
build(l,m,2*cur);
build(m+1,r,2*cur+1);
pushup(cur);
}
int queryI(int pl,int pr,int gcd,int cur)
{
int res;
if(pl<=tree[cur].l&&tree[cur].r<=pr&&tree[cur].val%gcd==0) return 0;
if(tree[cur].l==tree[cur].r) return tree[cur].l;
res=0;
if(pl<=tree[2*cur].r) res=queryI(pl,pr,gcd,2*cur);
if(res==0&&pr>=tree[2*cur+1].l) res=queryI(pl,pr,gcd,2*cur+1);
return res;
}
int queryII(int pl,int pr,int cur)
{
int res;
if(pl<=tree[cur].l&&tree[cur].r<=pr)
{
return tree[cur].val;
}
res=0;
if(pl<=tree[2*cur].r) res=getgcd(queryII(pl,pr,2*cur),res);
if(pr>=tree[2*cur+1].l) res=getgcd(queryII(pl,pr,2*cur+1),res);
return res;
}
void init()
{
int i,p,res,gcd;
mp.clear();
for(i=1;i<=n;i++)
{
p=i,gcd=num[p];
while(p<=n)
{
res=queryI(p,n,gcd,1);
if(res==0) res=n+1;
mp[gcd]+=(res-p);
gcd=getgcd(gcd,num[res]);
p=res;
}
}
}
int main()
{
int t,cas,i,l,r,gcd;
scanf("%d",&t);
for(cas=1;cas<=t;cas++)
{
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d",&num[i]);
}
build(1,n,1);
init();
scanf("%d",&q);
printf("Case #%d:\n",cas);
while(q--)
{
scanf("%d%d",&l,&r);
gcd=queryII(l,r,1);
printf("%d %lld\n",gcd,mp[gcd]);
}
}
return 0;
}