题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4777
题意:给出一个长度为n的数列,m个询问。每次询问区间[L,R]内有多少个数字与该区间内其他所有数字互质?
思路:计算每个数字向左向右能够延伸的最大区间[L,R]使得该区间内所有数字与该数字互质。并记录对于每个位置x,哪些数字的最左侧到达了x+1。然后输入所有询问,按照左区间升序。将每个区间依次插入树状数组。具体来说是这样。我们假设树状数组内某个区间内数字之和就是该区间的答案,也就是说某个数字i要修改的区间,该区间的所有数字与i互质。
(1)当到达某个位置x时,对于那些L[i]=x+1的位置,我们 add(i,1),add(R[i],-1)。因为过了x之后,这些i位置的数字就与所有BIT中的数字互质了。
(2)当要从某个位置i离开时,add(i,-1),add(R[i],1)。因为之前的(1)操作曾经插入过i,此时i过去之后要还原。
int prime[N],tag[N],cnt;
vector<int> factor[N];
void init()
{
int i,j;
for(i=2;i<N;i++) if(!tag[i])
{
prime[cnt++]=i;
for(j=i+i;j<N;j+=i) tag[j]=1;
}
for(i=2;i<N;i++)
{
int temp=i;
for(j=0;prime[j]*prime[j]<=temp;j++) if(temp%prime[j]==0)
{
factor[i].pb(prime[j]);
while(temp%prime[j]==0) temp/=prime[j];
}
if(temp>1) factor[i].pb(temp);
}
}
struct node
{
int L,R,id;
};
node a[N];
int d[N],L[N],R[N],n,m,c[N];
vector<int> Q[N];
void init1()
{
int i,j;
FOR0(i,N) Q[i].clear();
FOR0(i,N) c[i]=0;
FOR1(i,n)
{
L[i]=1;
FOR0(j,SZ(factor[d[i]]))
{
upMax(L[i],c[factor[d[i]][j]]+1);
c[factor[d[i]][j]]=i;
}
Q[L[i]-1].pb(i);
}
FOR0(i,N) c[i]=n+1;
for(i=n;i>=1;i--)
{
R[i]=n;
FOR0(j,SZ(factor[d[i]]))
{
upMin(R[i],c[factor[d[i]][j]]-1);
c[factor[d[i]][j]]=i;
}
}
}
int cmp(node a,node b)
{
return a.L<b.L;
}
int ans[N],S[N];
void add(int x,int val)
{
while(x<N) S[x]+=val,x+=x&-x;
}
int get(int x)
{
int ans=0;
while(x>0) ans+=S[x],x-=x&-x;
return ans;
}
int main()
{
init();
Rush(n)
{
RD(m);
if(!n&&!m) break;
int i,j,k;
FOR1(i,n) RD(d[i]);
FOR1(i,m) RD(a[i].L,a[i].R),a[i].id=i;
init1();
sort(a+1,a+m+1,cmp);
clr(S,0);
FOR1(i,n) if(L[i]==1) add(i,1),add(R[i]+1,-1);
int left=1;
FOR1(i,m)
{
while(left<a[i].L)
{
add(left,-1);
add(R[left]+1,1);
FOR0(j,SZ(Q[left]))
{
k=Q[left][j];
add(k,1);
add(R[k]+1,-1);
}
left++;
}
ans[a[i].id]=get(a[i].R)-get(a[i].L-1);
}
FOR1(i,m) PR(ans[i]);
}
}