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

来源:互联网 发布:港融大数据平台 编辑:程序博客网 时间:2024/06/16 09:51

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

110 1056 90 33 70 91 69 41 22 77 451 3 91 1 103 3 82 5 6 741 1 83 1 91 2 101 4 92 8 8 693 3 9

Sample Output

8012286

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;}
原创粉丝点击