zoukankan      html  css  js  c++  java
  • [BZOJ 4809] 相逢是问候

    Link:

    传送门

    Solution:

    以前没见过的套路题……

    1、使用EXT欧拉定理降幂的套路:

    $a^{x}=a^{xmodphi(P)+phi(P)} mod P$,$xge P$

    这样对于$c^{c^{c^x}}modP$就能递推/递归得套用上述定理计算,每层模数多套一层$phi$即可

    注意每次在快速幂时要判断当前指数是否大于当前模数才能用EXT!

    2、能证明一个数最多求$log$次$phi$就会变成1

    这样在$log$次内暴力更新,否则不管,就能保证$O(n*log^3)$

    3、复杂度中的3个$log$分别是:

    更新$log$次,每次更新迭代$log$层,每层要算一次快速幂

    明显只能优化快速幂。由于底不变,模数只有$log$种,想到分数的前后两部分预处理

    分块预处理出$[1,(1<<16)]$和$[1*(1<<16),(1<<16)*(1<<16)]$的答案以及与模数的大小关系

    这样每次拆出指数的前16位和后16位$O(1)$计算答案和大小关系就能做到$O(n*log^2)$

    4、听说原题数据锅了……

    虽然$phi(2)$和$phi(1)$都为1,但要更新到$phi(1)$!

    否则在最顶层指数为0时最终会迭代出$cmod2$而非$cmod1$,不一定为0!

    Code:

    #include <bits/stdc++.h>
    
    using namespace std;
    #define X first
    #define Y second
    #define pb push_back
    typedef double db;
    typedef long long ll;
    typedef pair<int,int> P;
    const int MAXN=1e5+10;
    ll pre[35][1<<16][2];
    bool f[35][1<<16][2];
    int n,m,p,c,dat[MAXN],phi[35],cnt;
    
    int getphi(int x)
    {
        int ret=x;
        for(int i=2;i*i<=x;i++)
            if(x%i==0)
            {
                ret=ret/i*(i-1);
                while(x%i==0) x/=i;
            }
        if(x!=1) ret=ret/x*(x-1);
        return ret;
    }
    ll quick_pow(ll a,ll b,ll MOD,bool &f)
    {
        ll ret=1;
        for(;b;b>>=1,a=a*a%MOD)
        {
            //判断是否大于某个数两个地方都要判! 
            if(b&1) f|=(ret*a>=MOD),ret=ret*a%MOD;
            f|=(a*a>=MOD&&b!=1);
        }
        return ret;
    }
    int QP(int x,int num,bool &flag)
    {
        int a=x&((1<<16)-1),b=x>>16;
        ll ret=1ll*pre[num][a][0]*pre[num][b][1];
        flag=f[num][a][0]|f[num][b][1]|(ret>=phi[num]);
        return ret%phi[num];
    }
    int cal(int x,int st)
    {
        int ret=x;
        if(ret>=phi[st])
            ret=ret%phi[st]+phi[st];
        while(st--)
        {
            bool f=0;
            ret=QP(ret,st,f);
            //注意特判,仅在指数>模数时可使用EXT欧拉定理 
            if(f&&st) ret+=phi[st];
        }
        return ret%p;
    }
    void PRE()
    {
        //分块预处理 
        for(int i=0;i<=cnt;i++)
        {
            pre[i][0][0]=pre[i][0][1]=1;
            if(phi[i]==1) f[i][0][0]=f[i][0][1]=1;
            pre[i][1][0]=quick_pow(c,1,phi[i],f[i][1][0]);
            int tmp=pre[i][1][1]=quick_pow(c,1<<16,phi[i],f[i][1][1]);
            
            for(int j=2;j<1<<16;j++)
            {
                f[i][j][0]=f[i][j-1][0]|(pre[i][j-1][0]*c>=phi[i]);
                pre[i][j][0]=pre[i][j-1][0]*c%phi[i];
                f[i][j][1]=f[i][j-1][1]|(pre[i][j-1][1]*tmp>=phi[i]);
                pre[i][j][1]=pre[i][j-1][1]*tmp%phi[i];
            }
        }
    }
    
    namespace SegmentTree
    {
        #define mid ((l+r)>>1)
        #define ls k<<1
        #define rs k<<1|1
        #define lc ls,l,mid
        #define rc rs,mid+1,r
        
        int sum[MAXN<<2],tag[MAXN<<2];
        void pushup(int k)
        {
            tag[k]=min(tag[ls],tag[rs]);
            sum[k]=(sum[ls]+sum[rs])%p;
        }
        void build(int k,int l,int r)
        {
            if(l==r)
            {sum[k]=dat[l]%p;tag[k]=0;return;}
            build(lc);build(rc);pushup(k);
        }
        void modify(int a,int b,int k,int l,int r)
        {
            if(tag[k]>=cnt) return;
            if(l==r)
            {sum[k]=cal(dat[l],++tag[k]);return;}
            if(a<=mid) modify(a,b,lc);
            if(b>mid) modify(a,b,rc);
            pushup(k);
        }
        int query(int a,int b,int k,int l,int r)
        {
            if(a<=l&&r<=b) return sum[k];
            int ret=0;
            if(a<=mid) (ret+=query(a,b,lc))%=p;
            if(b>mid) (ret+=query(a,b,rc))%=p;
            return ret;
        }    
    }
    using namespace SegmentTree;
    
    int main()
    {
        scanf("%d%d%d%d",&n,&m,&p,&c);
        for(int i=1;i<=n;i++)
            scanf("%d",&dat[i]);
        phi[0]=p;
        while(phi[cnt]!=1) 
            cnt++,phi[cnt]=getphi(phi[cnt-1]);
        //要迭代到phi(1)=1,而不能仅迭代到phi(2)=1
        //否则在x=0时最后一层会出现c%2 
        phi[++cnt]=1;PRE();
        
        build(1,1,n);
        while(m--)
        {
            int op,l,r;
            scanf("%d%d%d",&op,&l,&r);
            if(!op) modify(l,r,1,1,n);
            else printf("%d
    ",query(l,r,1,1,n));
        }
        return 0;
    }
  • 相关阅读:
    「NOIP 2017」列队
    java基础-略知一二
    重磅发布!阿里云推PostgreSQL 10 高可用版
    双11奇迹背后的大数据平台,不喧哗,自有声!
    深度解析双十一背后的阿里云 Redis 服务
    双11大考 POLARDB分钟级弹性让企业轻松扩展
    双十一流量洪峰 支撑阿里核心业务的云数据库揭秘
    双十一高并发场景背后的数据库RDS技术揭秘
    直击KubeCon 2018 |云原生正在改变你的衣食住行
    一文带你领略虚拟化领域顶级技术会议KVM Forum 2018
  • 原文地址:https://www.cnblogs.com/newera/p/10008910.html
Copyright © 2011-2022 走看看