• [LNOI] 相逢是问候 || 扩展欧拉函数+线段树


    原题为2017六省联考的D1T3

    给出一个序列,m次操作,模数p和参数c
    操作分为两种:
    1、将[l,r]区间内的每个数x变为(c^x)
    2、求[l,r]区间内数的和%p


    首先,我们要了解一些数论姿势:
    1、扩展欧拉定理
    //我们熟知的费马小定理用于p是质数,欧拉定理用于a,p互质,而这道题都不满足这个限制
    ((b>=phi(p)))时,(a^b=a^{bmod phi(p) + phi(p)})
    2、(其实不算数论姿势)一个数最多经过log此(phi)就会变成1

    所以我们发现,一个数在经过几次变化后,指数永远是(xmod1+1),也就是1,它就再也不变了!
    //上面这里不理解的话,可以考虑这样一道题
    //给定一个长度为n的序列a,每次询问区间[l,r] (a_l^{a_{l+1}^{…}})的值。(n<=10^5,m<=10^9)
    //原题应该是叫power tower

    所以我们建一棵线段树,维护当前区间和和这个区间中被修改的最小次数。
    预处理出对于mod p来讲,几次后一定会不变,记为k。每次修改将当前位置的修改次数++。如果这个区间的最小修改次数>=k,那么就不需要修改了,否则暴力修改。
    因为每次的修改次数不一样,所以要用初值a[i]计算times后当前数会变为什么。

    因为有n个数,每个数会被暴力修改(log(p))次,每次修改是(log(n))的,再乘上快速幂的复杂度(log)就是(O(nlog^3))
    这样基本能过了,但是被卡常的话就不稳了。可以考虑预处理c的快速幂,反正c是固定的……
    //bzoj不预处理就能过,洛谷过不了(也可能是博主写的比较菜)

    贴一份没有预处理的代码

    #include<cstdio>
    #include<algorithm>
    #define N 50010
    #define M 10010
    typedef long long ll;
    using namespace std;
    int a[N],n,m,prime[N],tot,p[N],P,c,op,l,r,k,MOD;
    bool np[N];
    struct hhh
    {
        int l,r,sum,is;
    }tre[4*N];
     
    int read()
    {
        int ans=0,fu=1;
        char j=getchar();
        for (;j<'0' || j>'9';j=getchar()) if (j=='-') fu=-1;
        for (;j>='0' && j<='9';j=getchar()) ans*=10,ans+=j-'0';
        return ans*fu;
    }
     
    int ksm(int x,int y,int mo,bool &flag)
    {
        int ret=1;
        bool big=0;
        while (y)
        {
        if (y&1)
        {
            flag|=big|((ll)ret*x>=mo);
            ret=(ll)ret*x%mo;
        }
        if ((ll)x*x>=mo) big=1;
        x=(ll)x*x%mo;
        y>>=1;
        }
        return ret;
    }
     
    void build(int i,int l,int r)
    {
        tre[i].l=l;tre[i].r=r;tre[i].is=0;
        if (l==r)
        {
        tre[i].sum=a[l]%P;
        return ;
        }
        int mid=(l+r)>>1;
        build(i*2,l,mid);
        build(i*2+1,mid+1,r);
        tre[i].sum=(tre[i*2].sum+tre[i*2+1].sum)%P;
    }
     
    int calc(int x,int dep)
    {
        int ret=x;
        if (ret>=p[dep]) ret=ret%p[dep]+p[dep];
        while (dep)
        {
        dep--;
        bool flag=0;
        ret=ksm(c,ret,p[dep],flag);
        if (flag) ret+=p[dep];
        }
        return ret%P;
    }
     
    void modify(int i,int l,int r)
    {
        if (l>tre[i].r || r<tre[i].l) return ;
        if (tre[i].is>=k) return ;
        if (tre[i].l==tre[i].r)
        {
        tre[i].is++;
        tre[i].sum=calc(a[tre[i].l],tre[i].is);
        return ;
        }
        modify(i*2,l,r);
        modify(i*2+1,l,r);
        tre[i].sum=(tre[i*2].sum+tre[i*2+1].sum)%P;
        tre[i].is=min(tre[i*2].is,tre[i*2+1].is);
    }
     
    int query(int i,int l,int r)
    {
        if (l>tre[i].r || r<tre[i].l) return 0;
        if (tre[i].l>=l && tre[i].r<=r) return tre[i].sum;
        return (query(i*2,l,r)+query(i*2+1,l,r))%P;
    }
     
    int phi(int n)
    {
        int res=n,a=n;
        for(int i=2; i*i<=a; i++)
        if(a%i==0)
        {
            res=res/i*(i-1);
            while (a%i==0) a/=i;
        }
        if (a>1) res=res/a*(a-1);
        return res;
    }
     
    int main()
    {
        n=read();m=read();P=read();c=read();
        for (int i=1;i<=n;i++) a[i]=read();
        p[0]=P;
        while (p[k]!=1) { ++k; p[k]=phi(p[k-1]); }
        p[++k]=1;
        build(1,1,n);
        while (m--)
        {
        op=read();l=read();r=read();
        if (op) printf("%d
    ",query(1,l,r));
        else modify(1,l,r);
        }
        return 0;
    }
    
  • 相关阅读:
    夜游遂宁滨江路
    易中天讲座十句人生感悟(发人深省,耐人寻味)
    遥望死海
    一直被忽略的成功之道:勤快并非优点,成功需要懒惰
    给别人以宽容,给自己以信心
    合理支配“财富”:经理人运用时间的12种典型模式
    三月的清晨
    学习的三重境界(想成功的人都不可错过)
    上班
    持续开发你的事业智慧:企业家保持冲锋势头的路径
  • 原文地址:https://www.cnblogs.com/mrha/p/8318947.html
Copyright © 2020-2023  润新知