zoukankan      html  css  js  c++  java
  • BZOJ4869:[SHOI2017]相逢是问候——题解

    http://www.lydsy.com/JudgeOnline/problem.php?id=4869

    题面复制于洛谷:https://www.luogu.org/problemnew/show/P3747#sub

     参考洛谷的前两篇(也是仅有的两篇)题解。

    首先我们要知道一个公式:

    这又被叫做扩展欧拉定理,证明我们并不关心。

    有了扩展欧拉定理,我们就能够避免高精度从而求出对于任意一个数的0操作之后变成什么数了。

    (递归或者迭代选一个,递归好理解,迭代有助于理解下面的题解,而且常数小)

    我们又有一个结论,对于一个p,它无限递归p=phi(p)直到p=1为止的深度为O(logp)。

    这样的好处在于我们虽然修改了很多次,但是当修改次数大于logp的时候,此时你再怎么修改也没有用了因为你的指数为1相当于没有操作。

    那么显然对于1我们记录该元素被操作了几次,然后暴力修改即可,可用线段树维护。复杂度O(nlognlogp)。(请注意这个复杂度是假的)

    这样的复杂度我们交到bzoj上是没有问题的,但是交到洛谷上会TLE3个点。将递归改成迭代,预处理每个p的phi,各种常数优化也会TLE2个点。

    emmm……why?

    当然是因为我们的复杂度没算对啊。

    对于单点修改,显然每次修改是O(logplogp)……等等,怎么多出来一个O(logp)。

    忘了我们使用了快速幂了吗,我们多出来的O(logp)就是这么来的。

    考虑除掉这个O(logp),显然预处理快速幂。

    如果你写的是迭代的话,你就会发现底数永远都是c不变,变的只是指数和模数, 且指数最大是p=1e8。

    我们可以先求出不同模数且指数<=1e5的c的幂,我们还可以求不同模数且指数=整1e5的c的幂。

    这就很像分块了,显然当我们要求指数为k时,k=x*1e5+y(y<1e5)显然可求。

    这样我们预处理出所有的数在多少次操作后的值,则我们的复杂度就是O(nlognlogp)。

    吐槽:最开始学完扩欧之后觉得这题洛谷给的难度高了,怎么就NOI+了,后来在TLE之后一看woc还有这种操作……

    神题神题……

    (然而博主并不想写正解,放的代码只能过bzoj,正解如果有时间的话会补上的emmm) 

    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    #include<cctype>
    #include<map>
    using namespace std;
    typedef long long ll;
    const int N=5e4+5;
    const int O=1e4+5;
    inline int read(){
        int X=0,w=0;char ch=0;
        while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
        while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
        return w?-X:X;
    }
    struct tree{
        ll v,t;
    }tr[N*4];
    int su[O],he[O],cnt,phi[40],n,m;
    ll p,c,logp,b[N];
    bool ok;
    inline ll qpow(ll k,int p){
        ll ans=1,s=c;
        while(k){
            if(k&1)ans=ans*s;
            s*=s;k>>=1;
            if(s>=p)ok=1,s%=p;
            if(ans>=p)ok=1,ans%=p;
        }
        return ans;
    }
    int Euler(int k){
        int res=k;
        for(int i=1;su[i]*su[i]<=k;i++){
            if(k%su[i]==0){
                res-=res/su[i];
                while(k%su[i]==0)k/=su[i];
            }
        }
        if(k>1)res-=res/k;
        return res;
    }
    void prime(){
        for(int i=2;i<O;i++){
            if(he[i]==0){
                cnt++;
                su[cnt]=i;
            }
            for(int j=1;j<=cnt&&i*su[j]<O;j++){
                he[su[j]*i]=1;
                if(i%su[j]==0)break;
            }
        }
        phi[logp]=p;
        while(phi[logp]!=1)phi[++logp]=Euler(phi[logp-1]);
        phi[++logp]=1;
    }
    void build(int a,int l,int r){
        if(l==r){
            tr[a].v=b[l]%p;
            return;
        }
        int mid=(l+r)>>1;
        build(a<<1,l,mid);build(a<<1|1,mid+1,r);
        tr[a].v=(tr[a<<1].v+tr[a<<1|1].v)%p;  
    }
    ll suan(ll v,ll k){
        ll tmp=v;
        if(tmp>phi[k])tmp=tmp%phi[k]+phi[k];
        for(int i=k;i>0;i--){
            ok=0;tmp=qpow(tmp,phi[i-1]);
            if(ok)tmp+=phi[i-1];
        }
        return tmp;
    }
    void gai(int a,int l,int r,int l1,int r1){
        if(tr[a].t>=logp)return;
        if(r<l1||r1<l)return;
        if(l==r){
            tr[a].t++;
            tr[a].v=suan(b[l],tr[a].t);
            return;
        }
        int mid=(l+r)>>1;
        gai(a<<1,l,mid,l1,r1);gai(a<<1|1,mid+1,r,l1,r1);
        tr[a].v=(tr[a<<1].v+tr[a<<1|1].v)%p;
        tr[a].t=min(tr[a<<1].t,tr[a<<1|1].t);
    }
    ll wen(int a,int l,int r,int l1,int r1){
        if(r<l1||r1<l)return 0;
        if(l1<=l&&r<=r1)return tr[a].v;
        int mid=(l+r)>>1;
        return (wen(a<<1,l,mid,l1,r1)+wen(a<<1|1,mid+1,r,l1,r1))%p;
    }
    int main(){
        n=read(),m=read(),p=read(),c=read();
        prime();
        for(int i=1;i<=n;i++)b[i]=read();
        build(1,1,n);
        for(int i=1;i<=m;i++){
            int op=read(),l=read(),r=read();
            if(!op)gai(1,1,n,l,r);
            else printf("%lld
    ",wen(1,1,n,l,r));
        }
        return 0;
    }

    +++++++++++++++++++++++++++++++++++++++++++

     +本文作者:luyouqi233。               +

     +欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+

    +++++++++++++++++++++++++++++++++++++++++++

  • 相关阅读:
    浅谈块元素绝对定位的margin属性
    因浏览器而异的空白节点(js清除空白节点)
    区分中英文字符的两种方法
    js中 visibility和display的区别 js中 visibility和display的区别
    观察者模式
    单例模式
    mongodb
    javascript的加载、解析、执行对浏览器渲染的影响
    EM算法原理
    YARN
  • 原文地址:https://www.cnblogs.com/luyouqi233/p/8454436.html
Copyright © 2011-2022 走看看