• [六省联考2017]相逢是问候(线段树+拓展欧拉定理)


    好题啊!

    调了一个中午,发现有一条语句 (RE) 了。在 (windows) 下没关系,(linux) 下有问题,大大的问题。

    while(phi[tot]!=1) phi[++tot]=calc_phi(phi[tot-1]);
    

    算是拓展欧拉定理的题吧。线段树只是一个工具,最主要还是暴力修改。因为 (varphi) 不断套下去最多会有 (lfloor log n floor) 层,所以我们对于每一层暴力算一遍,加上快速幂,时间复杂度 (O(nlog^3 n)),显然可能被卡。怎么优化呢?

    (O(log n)) 的快速幂换成 (O(1)) 的快速幂就好了,时间复杂度 (O(nlog^2 n))

    (Code Below:)

    #include <bits/stdc++.h>
    #define int long long
    #define lson (rt<<1)
    #define rson (rt<<1|1)
    using namespace std;
    const int maxn=100000+10;
    const int base=(1<<14)-1;
    int n,m,p,c,a[maxn],sum[maxn<<2],Min[maxn<<2],pw1[55][maxn],pw2[55][maxn],phi[maxn],tot;
    bool b1[55][maxn],b2[55][maxn],flag;
    
    inline int read(){
        register int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
        while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
        return (f==1)?x:-x;
    }
    
    int calc_phi(int n){
        int ans=n,m=sqrt(n);
        for(int i=2;i<=m;i++){
            if(n%i==0){
                ans=ans/i*(i-1);
                while(n%i==0) n/=i;
            }
        }
        if(n>1) ans=ans/n*(n-1);
        return ans;
    }
    
    void pre(){
        int tmp=p;phi[0]=p;
        while(tmp!=1) tmp=calc_phi(tmp),phi[++tot]=tmp;
        phi[++tot]=1;
        for(int i=0;i<=tot;i++){
            pw1[i][0]=1;
            for(int j=1;j<=base+1;j++){
                pw1[i][j]=pw1[i][j-1]*c;
                if(pw1[i][j]>=phi[i]) b1[i][j]=1,pw1[i][j]%=phi[i];
                b1[i][j]|=b1[i][j-1];
            }
        }
        for(int i=0;i<=tot;i++){
            pw2[i][0]=1;
            b2[i][1]=b1[i][base+1];
            for(int j=1;j<=base;j++){
                pw2[i][j]=pw2[i][j-1]*pw1[i][base+1];
                if(pw2[i][j]>=phi[i]) b2[i][j]=1,pw2[i][j]%=phi[i];
                b2[i][j]|=b2[i][j-1];
            }
        }
    }
    
    int calc(int a,int dep){
        flag=0;
        int x=a&base,y=(a>>14)&base;
        int ans=pw1[dep][x]*pw2[dep][y];
        if(ans>=phi[dep]) flag=1,ans%=phi[dep];
        flag|=b1[dep][x]|b2[dep][y];
        return ans;
    }
    
    int dfs(int a,int dep,int lim){
        flag=0;
        if(dep==lim){
            if(a>=phi[dep]) flag=1,a%=phi[dep];
            return a;
        }
        int b=dfs(a,dep+1,lim);
        return calc(flag?b+phi[dep+1]:b,dep);
    }
    
    inline void pushup(int rt){
        sum[rt]=(sum[lson]+sum[rson])%p;
        Min[rt]=min(Min[lson],Min[rson]);
    }
    
    void build(int l,int r,int rt){
        if(l == r){
            sum[rt]=a[l];
            return ;
        }
        int mid=(l+r)>>1;
        build(l,mid,lson);
        build(mid+1,r,rson);
        pushup(rt);
    }
    
    void update(int L,int R,int l,int r,int rt){
        if(Min[rt]>=tot) return ;
        if(l == r){
            Min[rt]++;
            sum[rt]=dfs(a[l],0,Min[rt]);
            return ;
        }
        int mid=(l+r)>>1;
        if(L <= mid) update(L,R,l,mid,lson);
        if(R > mid) update(L,R,mid+1,r,rson);
        pushup(rt);
    }
    
    int query(int L,int R,int l,int r,int rt){
        if(L <= l && r <= R){
            return sum[rt];
        }
        int mid=(l+r)>>1,ans=0;
        if(L <= mid) ans=(ans+query(L,R,l,mid,lson))%p;
        if(R > mid) ans=(ans+query(L,R,mid+1,r,rson))%p;
        return ans;
    }
    
    signed main()
    {
        n=read(),m=read(),p=read(),c=read();
        for(int i=1;i<=n;i++) a[i]=read();
        pre();build(1,n,1);
        int op,l,r;
        while(m--){
            op=read(),l=read(),r=read();
            if(op==0) update(l,r,1,n,1);
            if(op==1) printf("%lld
    ",query(l,r,1,n,1));
        }
        return 0;
    }
    
  • 相关阅读:
    微信公众号迁移配置注意点
    关于memcache 命令查看和 Telnet
    centOS 安装(光安装 和 u盘安装)
    CentOS删除自带的java,安装新java
    ubuntu常用命令
    ubuntu 的挂起与休眠
    saiku应用的调试
    数据挖掘123
    unbutu 安装java教程
    workbench的schema讲解一:(维度dimension设置的基本内容)
  • 原文地址:https://www.cnblogs.com/owencodeisking/p/10227494.html
Copyright © 2020-2023  润新知