• [bzoj 4869] [六省联考2017] 相逢是问候


    相逢是问候

    2017-09-09


    Description

    Informatik verbindet dich und mich.
    信息将你我连结。(s:看到这个就感觉有毒
    B君希望以维护一个长度为n的数组,这个数组的下标为从1到n的正整数。一共有m个操作,可以分为两种:0 l r表示将第l个到第r个数(al,al+1,...,ar)中的每一个数ai替换为c^ai,即c的ai次方,其中c是输入的一个常数,也就是执行赋值ai=c^ai1 l r求第l个到第r个数的和,也就是输出:sigma(ai),l<=i<=rai因为这个结果可能会很大,所以你只需要输出结果mod p的值即可。 

    Input

    第一行有三个整数n,m,p,c,所有整数含义见问题描述。
    接下来一行n个整数,表示a数组的初始值。
    接下来m行,每行三个整数,其中第一个整数表示了操作的类型。
    如果是0的话,表示这是一个修改操作,操作的参数为l,r。
    如果是1的话,表示这是一个询问操作,操作的参数为l,r。
    1 ≤ n ≤ 50000, 1 ≤ m ≤ 50000, 1 ≤ p ≤ 100000000, 0 < c <p, 0 ≤ ai < p 

    Output

    对于每个询问操作,输出一行,包括一个整数表示答案mod p的值。 

    Sample Input

    4 4 7 2
    1 2 3 4
    0 1 4
    1 2 4
    0 1 4
    1 1 3

    Sample Output

    0
    3
    1 40 19910626 2
    0 
    0 1 1
    1 1 1 
    0 1 1 
    1 1 1 
    0 1 1 
    1 1 1 
    0 1 1 
    1 1 1 
    0 1 1 
    1 1 1 
    0 1 1 
    1 1 1 
    0 1 1 
    1 1 1 
    0 1 1 
    1 1 1 
    0 1 1 
    1 1 1 
    0 1 1 
    1 1 1 
    0 1 1 
    1 1 1
    0 1 1
    1 1 1 
    0 1 1 
    1 1 1 
    0 1 1 
    1 1 1 
    0 1 1 
    1 1 1 
    0 1 1 
    1 1 1 
    0 1 1 
    1 1 1 
    0 1 1 
    1 1 1 
    0 1 1 
    1 1 1 
    0 1 1 
    1 1 1 
    in_put
    1 
    
    2 
    
    4 
    
    16 
    
    65536 
    
    11418102
    
    18325590
    
    13700558
    
    13700558
    
    13700558
    
    13700558
    
    13700558
    
    13700558
    
    13700558
    
    13700558
    
    13700558
    
    13700558
    
    13700558
    
    13700558
    
    13700558
    out_put

    考试时这个题感觉好神奇..当时某个学长讲的时候一脸mengbier,整个人都noip了..什么是φ?

    欧拉函数裸题,一个数c在mod一个数的时候,在最多log2p次会不变....

    所以线段树维护区间和,区间修改最小值;

    修改时遍历到每一个节点,暴力修改每一个改变的值,因为到一定的次数不会变,所以记录一个tag代表这个区间一共修改了多少次...

    当l==r时就是那个点修改次数...最多log2p次就不用改了,时间复杂度n*2log(n)。记住,欧拉不要乱mod,会出事的...

    #include<iostream>
    #include<cstdlib>
    #include<cstdio>
    #include<algorithm>
    #define ll (long long)
    #define LL long long 
    #define IN int 
    using namespace std;
    const IN maxn=50000+99;
    const IN N=10000+99;
    int read(){
        int an=0,f=1;
        char ch=getchar();
        while(!('0'<=ch&&ch<='9')){if(ch=='-')f=-f;ch=getchar();}
        while('0'<=ch&&ch<='9'){an=an*10+ch-'0';ch=getchar();}
        return an*f;
    }
    IN phi[35],a[maxn],F;
    IN n,m,p,c;
    IN prime[maxn],isp[maxn],k,maxp;//k是质数个数,ips说明这是和数 
    struct saber{
    IN sum,tag,l,r;
    }tr[maxn<<2];
    IN Phi(IN x){
        LL ans=x;
        for(IN i=1;i<=k && prime[i]*prime[i]<=x;i++){
            if(!(x%prime[i]))ans=ans*(prime[i]-1)/prime[i];
            while(!(x%prime[i]))x=x/prime[i];
        }
        if(x>1)ans=ans/x*(x-1);
        return ans;
    }
    void euler(){
        for(IN i=2;i<=N;i++){
            if(!isp[i])k++,prime[k]=i;
            for(IN j=1;j<=k;j++){
                if(i*prime[j]>N)break;
                isp[i*prime[j]]=1;
                if(!(i%prime[j]))break;
            }
        }
        phi[maxp]=p;
        while(phi[maxp]!=1)maxp++,phi[maxp]=Phi(phi[maxp-1]);
        maxp++;
        phi[maxp]=1;
    }
    void build(IN k,IN l,IN r){
        tr[k].l=l;
        tr[k].r=r;
        if(l==r){
            tr[k].sum=a[l];
            return ;
        }
        IN mid=(l+r)>>1;
        build(k<<1,l,mid);
        build(k<<1|1,mid+1,r);
        tr[k].sum=tr[k<<1].sum+tr[k<<1|1].sum;
    }
    IN ask(IN k,IN i,IN j){
        IN l=tr[k].l,r=tr[k].r;
        if(l==i&&r==j)return tr[k].sum;
        IN mid=(l+r)>>1;
        if(mid>=j)return ask(k<<1,i,j);
        else if(i>mid)return ask(k<<1|1,i,j);
        else return (ask(k<<1,i,mid)+ask(k<<1|1,mid+1,j))%p;
    }
    IN qw(LL k,IN mod){
        LL ans=1;LL s=c;
        while(k){
            if(k&1)ans*=s;
            k>>=1;
            s*=s;
            if(s>=mod)F=1,s%=mod;
            if(ans>=mod)F=1,ans%=mod;
        }
        return ans%mod;
    }
    LL work(LL a,LL t){
        LL tmp=a;
        if(tmp>phi[t])tmp=tmp%phi[t]+phi[t];
        for(IN i=t;i>0;i--){
            F=0;tmp=qw(tmp,phi[i-1]);
            if(F)tmp+=phi[i-1],F=0;
        }
        return tmp;
    }
    void change(IN k,IN i,IN j){
        if(tr[k].tag>=maxp)return;
        LL l=tr[k].l,r=tr[k].r;
        if(l==r){
            tr[k].tag++;
            tr[k].sum=work(a[l],tr[k].tag)%p;
            return;
        }
        IN mid=(l+r)>>1;
        if(mid>=j)change(k<<1,i,j);
        else if(i>mid)change(k<<1|1,i,j);
        else {change(k<<1,i,mid);change(k<<1|1,mid+1,j);}
        tr[k].sum=tr[k<<1].sum+tr[k<<1|1].sum;
        tr[k].tag=min(tr[k<<1].tag,tr[k<<1|1].tag);
    }
    int main(){
        n=read();m=read();p=read();c=read();
        for(IN i=1;i<=n;i++)a[i]=read();
        euler();
        build(1,1,n);
        while(m)
        {m--;
            IN x,y,z;
            x=read();y=read();z=read();
            if(x){
                printf("%lld
    ",ask(1,y,z)%p);
            }
            else {
                change(1,y,z);
            }
        }
        return 0;
    }
    相逢是问候

    by:s_a_b_e_r


    表示考试结束学长讲题的时候一脸memgbier+1

    欧拉定理+线段树维护

    求ccc……a[i] %p的值

    看上去一脸不可做,这要乘多少次

    然而这个世界上存在着一个神奇的定理——欧拉定理EXT

    a≡ax%φ(m)+φ(m)(mod m)

    既然我们要求ccx(mod p)

    不妨设d=c于是我们要求的就变成了cd

    据欧拉定理有cd=cd%φ(p)+φ(p)(mod p)

    即ccx=ccx%φ(p)+φ(p)(mod p)

    看不清?放大一下

    ccx=ccx%φ(p)+φ(p)(mod p)

    标记的部分是不是很眼熟?

    这部分又满足了欧拉定理

    于是再抽一次φ变成

    ccx%φ(φ(p))+φ(φ(p))%φ(p)+φ(p)(mod p)

    这样一直抽下去,最后要%的东西会变成φ(φ(φ(φ(……p)))) (特别多个φ)

    特别玄学的是这东西被φ多了就会变成1(据说最多log(p)次?)

    然后就会出现x%1+1 = 0+1 = 1的状况

    然后再对它修改就没什么用了,可以无视了

    于是一发线段树维护每个点被修改了多少次

    以及涉及欧拉的部分都不要乱%p,包括快速幂,容易出事qwq

    #include<iostream>
    #include<cstdio>
    #define ll long long
    using namespace std;
    const int N=50099,SP=10099;
    int pri[N],cp,phi[50],ci;
    bool isp[N],f;
    int n,m,p,c,a[N];
    struct tree{
    int l,r,tag;
    ll sum;
    }t[N<<2];
    void update(int k){
         t[k].sum=t[k<<1].sum+t[k<<1|1].sum;
         t[k].tag=min(t[k<<1].tag,t[k<<1|1].tag);
    }
    void build(int k,int l,int r){
         t[k].l=l,t[k].r=r;
         if(l==r){t[k].sum=a[l];return;}
         int mid=(l+r)>>1;
         build(k<<1,l,mid);
         build(k<<1|1,mid+1,r);
         update(k);
    }
    int Phi(int x){//求欧拉函数
        int ans=x;
        for(int i=1;i<=cp&&pri[i]*pri[i]<=x;++i)
        {
            if(!(x%pri[i]))ans=ans/pri[i]*(pri[i]-1);
            while(!(x%pri[i]))x/=pri[i];
        }
        if(x>1)ans=ans/x*(x-1);
        return ans;
    }
    void prime(){//欧拉筛素数
         for(int i=2;i<=SP;++i)
         {
             if(!isp[i])pri[++cp]=i;
             for(int j=1;j<=cp;++j)
             {
               if(i*pri[j]>N)break;
               isp[i*pri[j]]=1;
               if(i%pri[j]==0)break;
             }
         }
         phi[ci]=p;
         while(phi[ci]!=1)phi[++ci]=Phi(phi[ci-1]);
         phi[++ci]=1;
    }
    int kp(int x,int k,int mod){
        int ans=1;
        while(k)
        {
          if(k&1){
              if(1ll*ans*x>=mod)f=1;
              ans=1ll*ans*x%mod;
          }
          if(1ll*x*x>=mod)f=1;
          x=1ll*x*x%mod;
          k>>=1;
        }
        return ans;
    }
    int C(int a,int x){//抽φ 
        int tmp=a;
        if(tmp>phi[x])tmp=tmp%phi[x]+phi[x];//欧拉定理前提条件 
        for(int i=x;i>0;--i)
        {
          f=0;
          tmp=kp(c,tmp,phi[i-1]);
          if(f==1)tmp+=phi[i-1];
        }
        return tmp;
    }
    void change(int k,int L,int R){
        if(t[k].tag>=ci)return;
        int l=t[k].l,r=t[k].r;
        if(l==r)
        {
          t[k].sum=C(a[l],++t[k].tag);
          return;
        }
        int mid=(l+r)>>1;
        if(L<=mid)change(k<<1,L,R);
        if(R>mid)change(k<<1|1,L,R);
        update(k);
    }
    ll query(int k,int L,int R){
        int l=t[k].l,r=t[k].r;
        if(L<=l&&R>=r)return t[k].sum%p;
        if(r<L||l>R)return 0;
        return (query(k<<1,L,R)+query(k<<1|1,L,R))%p;
    }
    int main()
    {
        scanf("%d%d%d%d",&n,&m,&p,&c);
        for(int i=1;i<=n;++i)scanf("%d",&a[i]);
        build(1,1,n);
        prime();
        for(int i=1;i<=m;++i)
        {
          int type,l,r;
          scanf("%d%d%d",&type,&l,&r);
          if(!type)change(1,l,r);
          else printf("%lld
    ",query(1,l,r)%p);
        }
        return 0;
    }
    verbinden

    by:wypx


     s:省选后听见有人说谁mo谁是真***,今天我就在ans膜了个p,╮(╯▽╰)╭

    w:这说明了什么(笑)

     

    s:%数学dalao

    w:果然数学题还是要放⑨啊w

  • 相关阅读:
    Java之hashCode的作用和equals方法的重构规则
    Java-IO流之File操作和Properties操作
    Java-IO流之转换流的使用和编码与解码原理
    Java-IO 字节流的使用和效率比较
    Java-IO流之BufferedReader 和BufferedWriter的使用和原理
    二分查找
    leetcode530 二叉搜索树的最小绝对差
    leetcode94 二叉树的中序遍历
    leetcode17电话号码字母组合
    leetcode26 删除排序数组中的重复项
  • 原文地址:https://www.cnblogs.com/ck666/p/7499209.html
Copyright © 2020-2023  润新知