版权声明:转载注明下出处就行了。 https://blog.csdn.net/LJD201724114126/article/details/84947995
题目链接:哆啦A梦传送门
题意:给一串n个数字,m个询问,每次询问的区间中,与其他元素都互素的数字有多少个?
参考链接:https://www.cnblogs.com/kuangbin/p/3416181.html
https://www.cnblogs.com/shuguangzw/p/5272595.html
题解:这又是让你找互素,老办法,每次让你找有关互素的,你都不要去找,而去找不互素的,因为不互素我们可以通过质因子分解来解决。这题也是。
我们先每个数预处理出L,R区间,表示左右和这个数不互质的位置。这个只要从左到右和从右到左扫描一遍,分解质因素,找下一个质因素的位置。
然后对于每个查询进行离线处理,按照右端点排序。
更新的过程是这样的
(1)对于刚加入点x,树状数组L[x]位置+1 把这个定义为左更新
(2)对于所有R[i]=x的点,树状数组L[i]位置-1,i位置+1 把这个定义为右更新
(3)查询是询问区间 l->r的和
至于为什么要这样更新,这里题解用的方法确实很妙,实在很难简单用三言两语将清楚,我建议模拟一遍,不过代码里还是写了点注释。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
const int maxn=200010;
int prime[maxn],tot;
bool book[maxn];
void getprime()
{
memset(book,1,sizeof(book));
book[1]=tot=0;
for(int i=2;i<=200000;i++)
{
if(book[i]) prime[++tot]=i;
for(int j=1;j<=tot&&i*prime[j]<=200000;j++)
{
book[i*prime[j]]=0;
if(i%prime[j]==0) break;
}
}
}
int factor[100][2];
int fatcnt;
void getfactor(int x)
{
fatcnt=0;
int tmp=x;
for(int i=1;prime[i]<=tmp/prime[i];i++)
{
factor[fatcnt][1]=0;
if(tmp%prime[i]==0)
{
factor[fatcnt][0]=prime[i]; ///存储素因子
while(tmp%prime[i]==0){
factor[fatcnt][1]++; ///存储素因子个数
tmp/=prime[i];
}
fatcnt++;
}
}
if(tmp!=1){
factor[fatcnt][0]=tmp;
factor[fatcnt++][1]=1;
}
// return fatcnt;
}
int L[maxn],R[maxn];
int a[maxn];
int b[maxn];
int n,m;
int c[maxn];
void add(int i,int val)
{
if(i==0) return;
while(i<=n){
c[i]+=val;
i=i+(i&(-i));
}
}
int sum(int i)
{
int sum=0;
while(i>0)
{
sum+=c[i];
i-=(i&(-i));
}
return sum;
}
vector<int> vec[maxn];
struct node{
int l,r,index;
}num[maxn];
bool cmp(node x,node y){ return x.r<y.r;}
int ans[maxn],pp[maxn][15];
int main()
{
getprime();
// for(int i=1;i<=10;i++)
// printf("%d ",prime[i]);
// puts("");
while(scanf("%d%d",&n,&m))
{
if(m==0&&n==0) break;
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&num[i].l,&num[i].r);
num[i].index=i;
}
for(int i=1;i<maxn;i++) b[i]=n+1;
for(int i=n;i>=1;i--)
{
getfactor(a[i]);///得到a[i]的质因子
R[i]=n+1; ///假设为最右端+1
pp[i][0]=fatcnt; ///存储a[i]的素因子个数
for(int j=0;j<fatcnt;j++)
{
R[i]=min(R[i],b[factor[j][0]]);
b[factor[j][0]]=i;///标记质因子的位置
pp[i][j+1]=factor[j][0];///存储a[i]的素因子
}
}
for(int i=1;i<maxn;i++) b[i]=0;
for(int i=1;i<=n;i++)
{
L[i]=0; ///假设为最左端减1
fatcnt=pp[i][0];
for(int j=0;j<fatcnt;j++)
{
int item=pp[i][j+1];
L[i]=max(L[i],b[item]);
b[item]=i;
}
}
// for(int i=1;i<=n;i++)
// printf("L[%d]=%d ",i,L[i]);
// puts("");
// for(int i=1;i<=n;i++)
// printf("R[%d]=%d ",i,R[i]);
// puts("");
sort(num+1,num+1+m,cmp);
memset(c,0,sizeof(c));
for(int i=1;i<=n;i++){
c[i]=0;
vec[i].clear();
}
for(int i=1;i<=n;i++)
vec[R[i]].push_back(i); ///存储以R[i]为右端的点集
int id=1;
for(int i=1;i<=m;i++)
{
while(id<=n&&id<=num[i].r)
{
///我们在L[id]的位置加1,那么说明在当前以num[i].r为右端点的区间
///不互质数加1
add(L[id],1);
int SIZE=vec[id].size();
for(int j=0;j<SIZE;j++){///说明有以R[id]为界的点
int v=vec[id][j];
add(v,1);///我们在v位置加1,因为某个区间要查询到v,故要在这加1
add(L[v],-1);///我们在L[v]位置减1,因为前面已经加了1,故在这要减1
}
id++;
}
int len=sum(num[i].r)-sum(num[i].l-1);
ans[num[i].index]=num[i].r-num[i].l+1-len;
}
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]);
}
return 0;
}