题意:给出n个数,区间为(1,n),查询区间(l,r)的GCD,以及区间(1, n)有多少子区间的GCD等于区间(l,r)的GCD。
做法:使用线段树,或RMQ维护区间GCD,并且要预处理出所有可能出现的GCD值 的区间数量。针对固定左端点,一直向右GCD阶梯状减小的特性,大概有两种做法。
1)对于某一固定起点(以选定左端点L=i为例),从最右端R开始,二分枚举GCD突变位置pos,这一段阶梯的长度就是R-pos+1,定义INTERGCD(L,R)=区间(L,R)的GCD,则GCD值等于 INTERGCD(L,R)的区间数量就增加R-pos+1。
对于左端点i:令L=i,R=n。
a、l=L,r=R。
b、m=(l+r)/2,如果INTERGCD(L,m)<=INTERGCD(L,R),r=m;否则l=m+1;如果l==r,则阶梯突变位置就是l或r,记录pos=l,转c;否则重复b;
c、阶梯长度就是pos-R+1,则_map[INTERGCD(L,R)]+=pos-R+1;令R=pos-1,如果R>=L,转a,计算下一个阶梯;否则,以i为左端点的阶梯就计算完了。
2)以arr=(1,2,4,6,7)这列数为例,如图,数列下面是以某个i为端点的所有的GCD值,_tmp和_tmpnext是值为<GCD,数量>的map,_tmp记录当前列,各个GCD值的数量,_tmpnext记录下一行的_tmp值,_tmpnext由_tmp推得;对于第i-1列的_tmp的每一个元素iter(指针),_tmpnext[GCD(arr[i],iter->first)]+=iter->second。图中给出了两列所对应的_tmp(_tmpnext)的值,(可能第四列到第五列理解更清晰)。
对于第一种写法,因为在预处理的时候要询问INTERGCD(l,r),所以用线段树,预处理的时间复杂度就成了O(n(logn)^3),就会超时。用RMQ,O(1)的查询就可以了。第二种写法在处理的时候不需要询问INTERGCD,就无所谓了。
附上两种写法:calcu_interval是第一种写法,progress是第二种。
RMQ
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <map>
#include <string>
#include <cmath>
#define maxn 110000
#define LL long long
using namespace std;
int arr[maxn];
int _gcd[maxn][30];
map<int,LL>_map;
int _query(int l,int r)
{
int len=r-l+1;
int temp=(int)log2(double(len));
return __gcd(_gcd[l][temp],_gcd[r-(1<<temp)+1][temp]);
}
int _find(int l,int r,int n)
{
int gcd=_query(l,r);
int L=l,R=r;
while(l!=r)
{
int m=(l+r)/2;
if(_query(L,m)<=gcd)
{
r=m;
}
else
l=m+1;
}
int len=R-l+1;
_map[gcd]+=len;
return l;
}
void calcu_interval(int n)
{
_map.clear();
for(int i=1;i<=n;++i)
{
int l=i,r=n;
int pos=_find(l,r,n);
while(pos!=i)
{
r=pos-1;
pos=_find(l,r,n);
}
}
}
map<int,LL>_tmp,_tmpnext;
map<int,LL>::iterator iter;
void progress(int n)
{
_map.clear();
_tmp.clear();
for(int i=1;i<=n;++i)
{
_tmpnext.clear();
_tmpnext[arr[i]]++;
for(iter=_tmp.begin();iter!=_tmp.end();iter++)
{
int now=__gcd(arr[i],iter->first);
_tmpnext[now]+=iter->second;
}
for(iter=_tmpnext.begin();iter!=_tmpnext.end();iter++)
{
_map[iter->first]+=iter->second;
}
_tmp=_tmpnext;
}
}
int main()
{
int T,I=1;
scanf("%d",&T);
while(T--)
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%d",&arr[i]);
_gcd[i][0]=arr[i];
}
int nl=(int)log2(double(n));
for(int j=1;j<=nl;++j)
{
for(int i=1;i<=n;++i)
{
if(i+(1<<(j-1))<=n)
{
_gcd[i][j]=__gcd(_gcd[i][j-1],_gcd[i+(1<<(j-1))][j-1]);
}
}
}
// calcu_interval(n);
progress(n);
int m;
scanf("%d",&m);
printf("Case #%d:\n",I++);
for(int i=0;i<m;++i)
{
int l,r;
scanf("%d%d",&l,&r);
int gcd=_query(l,r);
printf("%d %lld\n",gcd,_map[gcd]);
}
}
}
线段树
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <map>
#include <string>
#include <cmath>
#define maxn 110000
#define LL long long
using namespace std;
int arr[maxn];
struct seg
{
int l,r;
int gcd;
seg *left,*right;
};
struct node
{
int v,w;
};
vector<node>head[maxn];
map<int,LL>_map;
seg *build(int l,int r)
{
int m=(l+r)/2;
seg *rt=new seg;
rt->l=l;
rt->r=r;
if(l==r)
{
rt->left=rt->right=NULL;
rt->gcd=arr[l];
return rt;
}
rt->left=build(l,m);
rt->right=build(m+1,r);
rt->gcd=__gcd(rt->left->gcd,rt->right->gcd);
return rt;
}
int query(seg *rt,int ql,int qr)
{
if(ql<=rt->l && rt->r<=qr)
{
return rt->gcd;
}
int l=rt->l;
int r=rt->r;
int m=(l+r)/2;
int gcd=0;
if(ql<=m)
gcd=query(rt->left,ql,qr);
if(qr>m)
gcd=(gcd==0)?query(rt->right,ql,qr):__gcd(gcd,query(rt->right,ql,qr));
return gcd;
}
map<int,LL>_tmp,_tmpnext;
map<int,LL>::iterator iter;
void progress(int n)
{
_map.clear();
_tmp.clear();
for(int i=0;i<n;++i)
{
_tmpnext.clear();
_tmpnext[arr[i]]++;
for(iter=_tmp.begin();iter!=_tmp.end();iter++)
{
int now=__gcd(arr[i],iter->first);
_tmpnext[now]+=iter->second;
}
for(iter=_tmpnext.begin();iter!=_tmpnext.end();iter++)
{
_map[iter->first]+=iter->second;
}
_tmp=_tmpnext;
}
}
void del(seg *rt)
{
if(rt!=NULL)
{
del(rt->left);
del(rt->right);
delete rt;
}
}
int main()
{
int T,I=1;
scanf("%d",&T);
while(T--)
{
int n;
scanf("%d",&n);
for(int i=0;i<n;++i)
{
scanf("%d",&arr[i]);
}
seg *root=build(0,n-1);
for(int i=0;i<n;++i)
{
head[i].clear();
}
progress(n);
int m;
scanf("%d",&m);
printf("Case #%d:\n",I++);
for(int i=0;i<m;++i)
{
int l,r;
scanf("%d%d",&l,&r);
int gcd=query(root,l-1,r-1);
printf("%d %lld\n",gcd,_map[gcd]);
}
del(root);
}
}