The 2021 ICPC Asia Regionals Online Contest (II) L Euler Function (數論,線段樹)


  • 題意:一個長度為\(n\)的序列\(x\)\(m\)次操作,有兩種,一種是對區間\([l,r]\)的數乘上\(w\),一種是詢問\([l,r]\)的每個數的歐拉函數之和。

  • 題解:首先看數據范圍,\(x[i]\)\(w\)的值都很小,最大才\(100\),根據歐拉函數公式:\(\phi [N]=N*(1-\frac{1}{p_1})*...*(1-\frac{1}{p_n})\)\(p_i\)表示\(N\)質因數分解后的質數,那么有如下的性質:

    對於一個質數P,\(\phi [N*P]=\phi [N]*P\ \ if(N|P)\)\(\phi [N*P]=\phi [N]*(P-1)\ \ if(N!|P)\)

    那么對於每個\(w\),先質因數分解,然后線段樹維護每個質因數,並開25個\(tag\)進行標記,如果當前區間全部包含某個質因數,就可以直接算。

  • 代碼

    #include <iostream>
    #include <vector>
    #include <set>
    #include <algorithm>
    #include <unordered_map>
    using namespace std;
    #define ll long long
    #define pb push_back
    const int N=1e5+10;
    const int mod=998244353;
    
    
    int n,m;
    ll x[N];
    ll res[200];  //歐拉函數值
    int cntp[200][200];  //記錄每個i的質數j的出現次數
    bool vis[200];
    int prime[100],cnt;
    
    void get_prime(int n){
        for(int i=2;i<=n;++i){
           if(!vis[i]) prime[cnt++]=i;
           for(int j=0;j<cnt && prime[j]<=n/i;++j){
               vis[i*prime[j]]=true;
               if(i%prime[j]==0) break;
           } 
        } 
    }
    
    struct Node{
        int l,r;
        ll val; ll mul;
        bool tag[26];
    }tr[N<<4];
    
    void push_up(int u){
        for(int i=0;i<25;++i){  //如果兩個兒子都有這個質因數,父親才有
            if(tr[u<<1].tag[i] && tr[u<<1|1].tag[i]){
                tr[u].tag[i]=true;
            }
        }
        tr[u].val=(tr[u<<1].val+tr[u<<1|1].val)%mod;
    }
    
    void push_down(int u){
        tr[u<<1].val=(tr[u<<1].val*tr[u].mul)%mod;
        tr[u<<1|1].val=(tr[u<<1|1].val*tr[u].mul)%mod;
        tr[u<<1].mul=(tr[u<<1].mul*tr[u].mul)%mod;
        tr[u<<1|1].mul=(tr[u<<1|1].mul*tr[u].mul)%mod;
        tr[u].mul=1;
    }
    
    void build(int u,int l,int r){
        if(l==r){
            tr[u]={l,r,res[x[l]],1};
            for(int i=0;i<25;++i){
                if(cntp[x[l]][i]) tr[u].tag[i]=true;
            }
            return;
        }
        tr[u]={l,r,1,1};
        int mid=(tr[u].l+tr[u].r)>>1;
        build(u<<1,l,mid);
        build(u<<1|1,mid+1,r);
        push_up(u);
    }
    
    void update(int u,int l,int r,int id,int cnt){
        if(tr[u].l>=l && tr[u].r<=r && tr[u].tag[id]){   //質因數出現過
            for(int i=1;i<=cnt;++i){
                tr[u].val=(tr[u].val*prime[id])%mod;
                tr[u].mul=(tr[u].mul*prime[id])%mod;
            }
            return;
        }
        if(tr[u].l==tr[u].r){    //質因數沒有出現過
            tr[u].val=(tr[u].val*(prime[id]-1))%mod; //先算一次
            for(int i=1;i<cnt;++i){
                tr[u].val=(tr[u].val*prime[id])%mod;
            }
            tr[u].tag[id]=true;
            return;
        }
        if(tr[u].mul!=1) push_down(u);
        int mid=(tr[u].l+tr[u].r)>>1;
        if(l<=mid) update(u<<1,l,r,id,cnt);
        if(r>mid) update(u<<1|1,l,r,id,cnt);
        push_up(u);
    }
    
    ll query(int u,int l,int r){
        if(tr[u].l>=l && tr[u].r<=r){
            return tr[u].val;
        }
        if(tr[u].mul!=1) push_down(u);
        int mid=(tr[u].l+tr[u].r)>>1;
        ll sum=0;
        if(l<=mid) sum=(sum+query(u<<1,l,r))%mod;
        if(r>mid) sum=(sum+query(u<<1|1,l,r))%mod;
        return sum;
    }
     
    int main(){
        get_prime(110);
        scanf("%d %d",&n,&m);
        for(int i=1;i<=n;++i){
            scanf("%lld",&x[i]);
        }
        for(int i=1;i<=100;++i){
            int tmp=i;
            res[i]=i;
            for(int j=0;j<25;++j){
                if(tmp%prime[j]==0){
                    res[i]=res[i]/prime[j]*(prime[j]-1);
                    while(tmp%prime[j]==0) tmp/=prime[j],cntp[i][j]++;
                }
            }
        }
        build(1,1,n);
        for(int i=1;i<=m;++i){
            int op;
            scanf("%d",&op);
            if(op==1){
                int l,r;
                scanf("%d %d",&l,&r);
                printf("%lld\n",query(1,l,r));
            }
            else{
                int l,r,x;
                scanf("%d %d %d",&l,&r,&x);
                for(int i=0;i<25;++i){
                    if(cntp[x][i]) update(1,l,r,i,cntp[x][i]);
                }
            }
        }
        return 0;
    }
    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM