JZOJ100045. 【NOIP2017提高A组模拟7.13】好数

Description

我们定义一个非负整数是“好数”,当且仅当它符合以下条件之一:
1.这个数是0或1
2.所有小于这个数且与它互质的正整数可以排成一个等差数列例如,8就是一个好数,因为1,3,5,7排成了等差数列。
给出N个非负整数,然后进行如下三个操作:
1.询问区间[L,R]有多少个好数
2.将区间[L,R]内所有数对S取余(S≤1000000)
3.将第C个数更改为X

分析

首先我们应该找出什么数是好数,
通过打表就可以发现,所以的质数,2的次幂,以及6。

对于操作1和操作3,我们都可以用线段树轻松地维护。
关键是操作2。

如果一个区间的最大值都小于取模的数的话,很显然这次取模是没有意义的,可以不做处理。
可以证明一个数最多会被有效取模log次,
那么时间复杂度就可以保证了。

code

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string.h>
#include <cmath>
#include <math.h>
#define N 1000003
using namespace std;
int n,m,a[N],tr[N*4],mx[N*4],ss[N];
int x,y,z;
bool bz[N*10];
char ch;
void read(int &n)
{
    n=0;
    ch=getchar();
    while((ch<'0' || ch>'9') && ch!='-')ch=getchar();
    int w=1;
    if(ch=='-')w=-1,ch=getchar();
    while('0'<=ch && ch<='9')n=n*10+ch-'0',ch=getchar();
    n*=w;
}
void make(int x,int l,int r)
{
    if(l==r)
    {
        tr[x]=bz[a[l]];
        mx[x]=a[l];
        return;
    }
    int m=(l+r)/2;
    make(x+x,l,m);
    make(x+x+1,m+1,r);
    tr[x]=tr[x+x]+tr[x+x+1];
    mx[x]=max(mx[x+x],mx[x+x+1]);
}
int find(int x,int l,int r,int ll,int rr)
{
    if(ll==l && rr==r)return tr[x];
    int m=(l+r)/2;
    if(rr<=m)return find(x+x,l,m,ll,rr);else
        if(ll>m)return find(x+x+1,m+1,r,ll,rr);else
            return find(x+x,l,m,ll,m)+find(x+x+1,m+1,r,m+1,rr);
}
void work(int x,int l,int r,int z)
{
    if(mx[x]<z)return;
    if(l==r)
    {
        mx[x]=mx[x]%z;
        tr[x]=bz[mx[x]];
        return;
    }
    int m=(l+r)/2;
    work(x+x,l,m,z);
    work(x+x+1,m+1,r,z);
    tr[x]=tr[x+x]+tr[x+x+1];
    mx[x]=max(mx[x+x],mx[x+x+1]);
}
void change(int x,int l,int r,int ll,int rr,int z)
{
    if(mx[x]<z)return;
    if(ll==l && rr==r)
    {
        work(x,l,r,z);
        return;
    }
    int m=(l+r)/2;
    if(rr<=m)change(x+x,l,m,ll,rr,z);else
        if(ll>m)change(x+x+1,m+1,r,ll,rr,z);else
            change(x+x,l,m,ll,m,z),change(x+x+1,m+1,r,m+1,rr,z);
    tr[x]=tr[x+x]+tr[x+x+1];
    mx[x]=max(mx[x+x],mx[x+x+1]);
}
void chan(int x,int y,int l,int r)
{
    if(l==r)
    {
        mx[x]=z;
        tr[x]=bz[mx[x]];
        return;
    }
    int m=(l+r)/2;
    if(y<=m)chan(x+x,y,l,m);
        else chan(x+x+1,y,m+1,r);
    tr[x]=tr[x+x]+tr[x+x+1];
    mx[x]=max(mx[x+x],mx[x+x+1]);
}
void write(int x)
{
     if(x>9) write(x/10);
     putchar(x%10+'0');
}
int main()
{
    memset(bz,1,sizeof(bz));
    for(int i=2;i<=1000000;i++)
    {
        if(bz[i])
            ss[++ss[0]]=i;
        for(int j=1;j<=ss[0];j++)
        {
            if(ss[j]*i>1000000)break;
            bz[ss[j]*i]=0;
            if(i%ss[j]==0)break;
        }
    }
    for(int i=2;i<=1000000;i=i*2)
        bz[i]=1;
    bz[6]=1;
    freopen("good8.in","r",stdin);
    freopen("good.out","w",stdout);
    read(n);read(m);
    for(int i=1;i<=n;i++)
        read(a[i]);
    make(1,1,n);
    for(int k=1;k<=m;k++)
    {
        read(x);
        read(y);
        read(z);
        if(x==1)write(find(1,1,n,y,z)),printf("\n");else
        if(x==2)read(x),change(1,1,n,y,z,x);else
            chan(1,y,1,n);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值