这道题题目挺容易看懂的,其实难度也不是很大,但是我竟然用了一个晚上+半个上午做这道题。
首先,我们可以预处理出对于每个数,他左边第一个与他不互质的数的位置和他右边第一个与他不互质的数的位置。
处理方法:
我们先线筛,在线筛的同时记录筛每个数的那个质因子(from)。
然后我们按照编号从小到达处理每一个数,用f[i]记录当前是i的倍数的最靠右的数的编号。
我们每用from找到一个质因子,就把f[i]和当前的数的编号pos之间的关系处理好。
具体代码:
int f[maxn];
void get_p(int x,int pos) {
if(x==1) return ;
int y;
do {
y=from[x];
node[pos].x[1]=max(node[pos].x[1],f[y]);//更新pos左边第一个与他不互质的数
node[f[y]].x[2]=min(node[f[y]].x[2],pos);//更新f[y]右边第一个与他不互质的数
f[y]=pos;while(x%y==0) x/=y;
}while(x!=1);
}
我们用x[0]表示数的编号(位置),x[1]表示左边第一个与他不互质的数的位置,x[2]表示右边第一个与他不互质的数的位置。
这是一个三维的问题。我们需要找到满足$l \leq x_0 \leq r$且$ x_1 < l $且$ x_2 > r $的数的个数。
因为$ x_1<x_0<x_2 $,所以我们可以降维。
怎么降维呢?
准确来说就是按照容斥列式子:
$ [ x_1 < l \land l \leq x_0 \leq r \land x_2 > r ] $
$ = [ l \leq x_0 \leq r ] \\ \ \ - [ l \leq x_0 \leq r \land x_1 \geq l ] \\ \ \ - [ l \leq x_0 \leq r \land x_2 \leq r ] \\ \ \ + [ l \leq x_0 \leq r \land x_1 \geq l \land x_2 \leq r ] $
$ = [ l \leq x_0 \leq r ] \\ \ \ - ( [ x_0 \leq r \land x_1 \geq l ] - [ x_0 < l \land x_1 \geq l ] ) \\ \ \ - ( [ l \leq x_0 \land x_2 \leq r ] - [ x_0 > r \land x_2 \leq r ] ) \\ \ \ + [ x_1 \geq l \land x_2 \leq r ] $
$ = [ l \leq x_0 \leq r ] \\ \ \ - [ x_0 \leq r \land x_1 \geq l ] \\ \ \ - [ l \leq x_0 \land x_2 \leq r ] \\ \ \ + [ x_1 \geq l \land x_2 \leq r ] $
然后直接用树状数组算这3个东西就可以了。
//Serene
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
using namespace std;
const int maxn=2e5+10;
int n,m,ans[maxn];
int aa;char cc;
int read() {
aa=0;cc=getchar();
while(cc<'0'||cc>'9') cc=getchar();
while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
return aa;
}
struct Node{
int x[3];
}node[maxn];
bool cmp1(const Node& a,const Node& b) {
return a.x[2]<b.x[2];
}
struct Ask {
int x[3];
}ask[maxn];
bool cmp2(const Ask& a,const Ask& b) {
return a.x[2]<b.x[2];
}
int prime[maxn/5],totp,from[maxn];
bool ok[maxn];
void get_prime() {
for(int i=2;i<=maxn-5;++i) {
if(!ok[i]) prime[++totp]=i,from[i]=i;
for(int j=1;j<=totp&&prime[j]<=(maxn-5)/i;++j) {
ok[i*prime[j]]=1;from[i*prime[j]]=prime[j];
if(i%prime[j]==0) break;
}
}
}
int f[maxn];
void get_p(int x,int pos) {
if(x==1) return ;
int y;
do {
y=from[x];
node[pos].x[1]=max(node[pos].x[1],f[y]);
node[f[y]].x[2]=min(node[f[y]].x[2],pos);
f[y]=pos;while(x%y==0) x/=y;
}while(x!=1);
}
int sz[maxn];
void add(int pos) {while(pos<=n+1) sz[pos]++,pos+=(pos&-pos);}
int q(int pos) {int rs=0; while(pos) rs+=sz[pos],pos-=(pos&-pos); return rs;}
int main() {
n=read();m=read();
get_prime();int x,pos;
while(n||m) {
memset(f,0,sizeof(f));
for(int i=1;i<=n;++i) {
node[i].x[1]=0;node[i].x[2]=n+1;node[i].x[0]=i;
x=read(); get_p(x,i);
}
for(int i=1;i<=m;++i) {
ask[i].x[1]=read();ask[i].x[2]=read();
ask[i].x[0]=i;ans[i]=ask[i].x[2]-ask[i].x[1]+1;
}
sort(ask+1,ask+m+1,cmp2);
memset(sz,0,sizeof(sz));pos=1;
for(int i=1;i<=m;++i) {
x=ask[i].x[0];
while(pos<=n&&node[pos].x[0]<=ask[i].x[2]) add(node[pos].x[1]+1),pos++;
ans[x]-=(pos-1-q(ask[i].x[1]));
}
sort(node+1,node+n+1,cmp1);
memset(sz,0,sizeof(sz));pos=1;
for(int i=1;i<=m;++i) {
x=ask[i].x[0];
while(pos<=n&&node[pos].x[2]<=ask[i].x[2]) add(node[pos].x[0]+1),pos++;
ans[x]-=(pos-1-q(ask[i].x[1]));
}
memset(sz,0,sizeof(sz));pos=1;
for(int i=1;i<=m;++i) {
x=ask[i].x[0];
while(pos<=n&&node[pos].x[2]<=ask[i].x[2]) add(node[pos].x[1]+1),pos++;
ans[x]+=(pos-1-q(ask[i].x[1]));
}
for(int i=1;i<=m;++i) printf("%d\n",ans[i]);
n=read();m=read();
}
return 0;
}