【题目】
题目描述:
我们定义一个非负整数是“好数”,当且仅当它符合以下条件之一:
1. 这个数是 0 或 1。
2. 所有小于这个数且与它互质的正整数可以排成一个等差数列,例如,8 就是一个好数,因为 1,3,5,7 排成了等差数列。
给出 N 个非负整数,然后进行如下三个操作:
1. 询问区间 [ L , R ] 有多少个好数。
2. 将区间 [ L , R ] 内所有数对 S 取余(S ≤ 1000000)。
3. 将第 C 个数更改为 X 。
提示:如果你不知道如何判断一个数是否为好数,你可以打个表找找规律。
输入格式:
第一行包含两个正整数 N 和 M ,M 表示操作数目。
第二行包含 N 个非负整数。
接下来的 M 行每行表示 1 个操作:“1 L R”表示第 1 个操作,“2 L R S”表示第 2 个操作,“3 C X”表示第 3 个操作。
输出格式:
对每个操作1,输出一个非负整数,表示区间内好数的个数。
样例数据:
【样例1】
输入
3 6 4 6 9 1 1 3 1 3 3 2 1 1 10 1 1 3 3 2 4 1 1 3
输出
2 0 2 2
【样例2】
输入
8 5 12 24 17 31 16 21 18 30 1 2 5 2 4 7 7 3 2 13 1 1 8 1 3 6
输出
3 6 4
备注:
数据规模与约定:
【分析】
首先我们分析一下哪些数是好数,我是先打了一个表(100以内):
0 1 2 3 4 5 6 7 8 11 13 16 17 19 23 29 31 32 37 41 43 47 53 59 61 64 67 71 73 79 83 89 97
这样还是不好找规律,我们再重新排一下:
0 1 6
2 4 8 16 32 64
3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
这样一来,我们发现0、1、6、质数、2的k次幂的数都是好数(证明我不是很会,感性理解一下就行了)
然后我们就可以做题了,通过题意和数据范围我们很容易想到要用线段树
但是对于 2 操作(区间取余),貌似不能用 lazy tag,直接枚举的话又要超时,怎么办呢?
我们直接存一下每个区间的最大值,如果这个值比模数还小,那么就直接跳过这个区间
然后直接套线段树模板就可以了,虽说这个算法还是比较暴力,但还是能过的
(还有一个性质:一个数取余后要么不变化,要么小于它的1/2,听说它有用但我好像没用到)
【代码】
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=100005,M=1000005;
int n,m;
int a[N],sum[4*N],maxn[4*N],prime[M];
bool mark[M];
void init()
{
int i,j,sum=0;
memset(mark,true,sizeof(mark));
for(i=2;i<=M;++i)
{
if(mark[i]) prime[++sum]=i;
for(j=1;j<=sum&&i*prime[j]<=M;++j)
{
mark[i*prime[j]]=false;
if(i%prime[j]==0) break;
}
}
mark[6]=true;
for(i=4;i<=M;i<<=1)
mark[i]=true;
}
void build(int k,int l,int r)
{
if(l==r)
{
sum[k]=mark[a[l]];
maxn[k]=a[l];
return;
}
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
sum[k]=sum[k<<1]+sum[k<<1|1];
maxn[k]=max(maxn[k<<1],maxn[k<<1|1]);
}
int query(int k,int l,int r,int x,int y)
{
if(l>=x&&r<=y) return sum[k];
int s=0,mid=(l+r)>>1;
if(x<=mid) s+=query(k<<1,l,mid,x,y);
if(y>mid) s+=query(k<<1|1,mid+1,r,x,y);
return s;
}
void mod(int k,int l,int r,int x,int y,int s)
{
if(maxn[k]<s) return;
if(l==r)
{
maxn[k]%=s;
sum[k]=mark[maxn[k]];
return;
}
int mid=(l+r)>>1;
if(x<=mid) mod(k<<1,l,mid,x,y,s);
if(y>mid) mod(k<<1|1,mid+1,r,x,y,s);
sum[k]=sum[k<<1]+sum[k<<1|1];
maxn[k]=max(maxn[k<<1],maxn[k<<1|1]);
}
void change(int k,int l,int r,int x,int y)
{
if(l==r)
{
sum[k]=mark[y];
maxn[k]=y;
return;
}
int mid=(l+r)>>1;
if(x<=mid) change(k<<1,l,mid,x,y);
else change(k<<1|1,mid+1,r,x,y);
sum[k]=sum[k<<1]+sum[k<<1|1];
maxn[k]=max(maxn[k<<1],maxn[k<<1|1]);
}
int main()
{
// freopen("good.in","r",stdin);
// freopen("good.out","w",stdout);
int x,y,i,p,s;
scanf("%d%d",&n,&m);
for(i=1;i<=n;++i)
scanf("%d",&a[i]);
init();
build(1,1,n);
for(i=1;i<=m;++i)
{
scanf("%d",&p);
if(p==1)
{
scanf("%d%d",&x,&y);
printf("%d\n",query(1,1,n,x,y));
}
if(p==2)
{
scanf("%d%d%d",&x,&y,&s);
mod(1,1,n,x,y,s);
}
if(p==3)
{
scanf("%d%d",&x,&y);
change(1,1,n,x,y);
}
}
// fclose(stdin);
// fclose(stdout);
return 0;
}