【hdu5634】Rikka with Phi(线段树+欧拉函数)

Description

    Rikka and Yuta are interested in Phi function (which is known as Euler’s totient function).
    Yuta gives Rikka an array A[1..n] of positive integers, then Yuta makes m queries.
    There are three types of queries:
        1 l r
    Change A[i] into φ(A[i]), for all i∈[l,r].
        2 l r x
    Change A[i] into x, for all i∈[l,r].
        3 l r
    Sum up A[i], for all i∈[l,r].
    Help Rikka by computing the results of queries of type 3.

Input

    The first line contains a number T(T100) ——The number of the testcases. And there are no more than 2 testcases with n>105
    For each testcase, the first line contains two numbers n,m( n3105,m3105 )。
    The second line contains n numbers A[i] .Each of the next m lines contains the description of the query.It is guaranteed that 1A[i]107 At any moment.

Output

    For each query of type 3, print one number which represents the answer.

Sample Input

1
10 10
56 90 33 70 91 69 41 22 77 45
1 3 9
1 1 10
3 3 8
2 5 6 74
1 1 8
3 1 9
1 2 10
1 4 9
2 8 8 69
3 3 9

Sample Output

80
122
86

I think

    题意
    1)一般线段树的加和操作
    2)将节点值x改为 φ(x)
    算法:线性筛欧拉函数+线段树
    实现:作为一只第一次用到欧拉函数的蒟蒻,默默去膜了大佬们的筛法讲解,安利:几种常用的数学算法 线性筛欧拉函数
    最后筛欧拉函数的代码就是这样啦

void Euler() {
    memset(isp,1,sizeof(isp));
    p[0]=0,isp[1]=0,phi[1]=1;
    for(int i=2;i<sm;++i) {
        if(isp[i]) p[++p[0]]=i,phi[i]=i-1;
        for(int j=1;j<=p[0]&&i*p[j]<=sm;++j) {
            isp[i*p[j]]=0;
            if(i%p[j]==0) { phi[i*p[j]]=phi[i]*p[j];break;}
            phi[i*p[j]]=phi[i]*(p[j]-1);
        } 
    }
}

    而我们知道,对于一个数n反复取欧拉函数,它可以在 log(n) 的时间内最终变为1,如果有连着一大段一大段的1不是我们喜闻乐见的吗?——就可以把它们合作一个区间更新啦,因此在执行操作1时,更新需要满足的条件不再仅是ll<=l&&r<=rr,还需要再设一个标记表示是否整个区间均为1,同时满足k[i]==1即可,否则继续递归到满足要求为止。

Code

#include<cstdio>
#include<cstring>
using namespace std;
typedef long long LL;
const LL sm = 1e7+10;
const LL mx = 1200000+50;
LL n,m,T;
LL v,s[mx],k[mx];
int isp[sm],phi[sm],p[700000];
char ch;
template <typename T> void read(T &x) {
    x=0,ch=getchar();
    while(ch>'9'||ch<'0') ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
void Euler() {
    memset(isp,1,sizeof(isp));
    p[0]=0,isp[1]=0,phi[1]=1;
    for(int i=2;i<sm;++i) {
        if(isp[i]) {
            p[++p[0]]=i;
            phi[i]=i-1;
        }
        for(int j=1;j<=p[0]&&i*p[j]<=sm;++j) {
            isp[i*p[j]]=0;
            if(i%p[j]==0) {
                phi[i*p[j]]=phi[i]*p[j];
                break;
            }
            phi[i*p[j]]=phi[i]*(p[j]-1);
        } 
    }
}
void pushup(LL i,LL l,LL r) {
    LL ls=i<<1,rs=i<<1|1;
    s[i]=s[ls]+s[rs];
    if(k[ls]==k[rs]) k[i]=k[ls];
    else k[i]=0;
}
void pushdown(LL i,LL l,LL r) {
    LL m=(l+r)>>1,ls=i<<1,rs=i<<1|1;
    k[ls]=k[rs]=k[i];
    s[ls]=(m-l+1)*k[ls];
    s[rs]=(r-m)*k[rs];
}
void build(LL i,LL l,LL r) {
    s[i]=k[i]=0;
    if(l==r) {
        read(v),s[i]=k[i]=v;return;
    }
    LL m=(l+r)>>1;
    build(i<<1,l,m);
    build(i<<1|1,m+1,r);
    pushup(i,l,r);
}
void modify(LL i,LL l,LL r,LL ll,LL rr,LL val) {
    if(ll<=l&&r<=rr) {
        s[i]=(r-l+1)*val,k[i]=val;
        return ;
    }
    if(k[i])pushdown(i,l,r);
    LL m=(l+r)>>1;
    if(ll<=m) modify(i<<1,l,m,ll,rr,val);
    if(rr> m) modify(i<<1|1,m+1,r,ll,rr,val);
    pushup(i,l,r);
}
void euler(LL i,LL l,LL r,LL ll,LL rr) {
    if(k[i]&&ll<=l&&r<=rr) {
        k[i]=phi[k[i]],s[i]=1ll*k[i]*(r-l+1);
        return;
    }
    if(l==r)return;
    if(k[i]) pushdown(i,l,r);
    LL m=(l+r)>>1;
    if(ll<=m) euler(i<<1,l,m,ll,rr);
    if(rr> m) euler(i<<1|1,m+1,r,ll,rr);
    pushup(i,l,r);
}
LL query(LL i,LL l,LL r,LL ll,LL rr) {
    if(l>r)return 0;
    if(ll<=l&&r<=rr) return s[i];
    if(k[i]) pushdown(i,l,r);
    LL m=(l+r)>>1;
    LL ans=0;
    if(ll<=m) ans+=query(i<<1,l,m,ll,rr);
    if(rr> m) ans+=query(i<<1|1,m+1,r,ll,rr);
    pushup(i,l,r);
    return ans;
}
int main() {
    Euler();
    read(T);
    LL ind,ll,rr,val;
    while(T--) {
        read(n),read(m);
        build(1,1,n);
        for(LL i=1;i<=m;++i) {
            read(ind),read(ll),read(rr);
            if(ind==2) read(val),modify(1,1,n,ll,rr,val);
            else if(ind==1) euler(1,1,n,ll,rr);
            else printf("%lld\n",query(1,1,n,ll,rr));
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值