4869: [Shoi2017]相逢是问候 (欧拉降幂)

Description

Informatikverbindetdichundmich.

信息将你我连结。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.直接放弃    因为要更新5e4次,一共5e4个点--->我放弃了,不可能的,如果区间更新的话,还是要算到每一个点,所以
              还是点更新,复杂度根本不够怎么办,肯定有别的方法,自己没学---->好,看看题解
              mmp,每个点更新很少的几次之后就不会更新了(标记区域,之后就相当于是区间更新,到了定值,就不更新了)  
2.欧拉定理    (1)原理是什么 (2)条件是什么 (3)最后终结在哪里
              (1)到现在也不是很清楚,只知道,gcd(a,p)==1,时降幂的原理,也就是 费马小定理,至于拓展的,记住结论吧
              (2)条件是 a^(x) mod(p)   -----> 变为[a^(x mod(phi(p))+x>=phi(p)?phi(p):0]mod(p)
                  (2.2)还有就是x>=phi(p)这个条件, 这个x可不是 mod(*) 之后的值,它是之前的值,所以不能直接比较,只能在快速幂,那里比较一下
              (3) 一个p,经过至多 log(p)次之后,就会变成1, 我们知道任意一个数值mod(1)都是0,所以这也就奠定了终结的条件
                  (3.2)但是最后是直接算到 phi(*)=1,就结束了么,出题人就是这么错的,导致少了一层 比如 2^(2^0) mod3 它是2
                        而2^(2^(2^0)) mod 3,为 1,哪个才是定值呢,应该是后面一个,因为 最后一个要是mod phi(1)的话,说明
                        第一个加的C 肯定就是 mod 1了,也就是肯定是恒定的了, 但是如果第一个C mod2的话,那就不一定了,所以
                        最后要算到phi(1),也就是 val%phi(1)+ [phi(1)], C^(*) mod 1,这样的话,肯定就是定值了,
                        如果多算几层,肯定也行,只不过,人都喜欢精简,也就是最小的层数到底是几
 */

#include<bits/stdc++.h>
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
typedef long long ll;
using namespace std;

 
const int N=50005;
int n,m,P,DEP;
ll a[N],c,Phi[N];
int dep[N];
 
struct trment{
      int l,r;
      ll sum;
	  int cnt;
}tr[N<<2];
 
void pushup(int id){
    tr[id].sum=(tr[id<<1].sum+tr[id<<1|1].sum)%P;
    tr[id].cnt=min(tr[id<<1].cnt,tr[id<<1|1].cnt);
}
 
int phi(int x){
    int m=sqrt(x+0.5);
    int ans=x;
    for(int i=2;i<=m;i++){
        if(x%i==0){
            ans-=ans/i;
            while(x%i==0)x/=i;
        }
    }
    if(x>1)ans-=ans/x;
    return ans;
}

ll pow_(int flag,int n,ll mod){
    ll ans=1,base=c;
    if(ans>=mod)flag=1;
    while (n){
        if (n&1){
        	ans=ans*base%mod;if(ans>=mod)flag=1;
		}
		   
        n>>=1;
        base=base*base%mod; if(base>=mod)flag=1;
    }
    return ans;
}

ll Cal(int t,ll x){//????判断大小 
    ll res=x;
    if(res>Phi[t])res=res%Phi[t]+Phi[t];
    while(t){
    	int flag=0;//只能这么将就了,不会别的了 
        res=pow_(flag,res,Phi[t-1]);
        if(flag||res>=Phi[t-1]||res==0)res+=Phi[t-1];
		//防止有一种情况就是取余之后,直接为0了,那他肯定是 Phi[t-1]的倍数,所以需要判断是否为0 
		//那他肯定大于Phi[t-1],但还是不能保证它是100%正确 
		//%Phi[t-1]的话,那第二个判断条件就没有用了,所以只能在快速幂里面判断 
		t--;
    }
    return res%Phi[0];
}

void build(int id,int l,int r){
    tr[id].l=l,tr[id].r=r;
    if (l==r){
        tr[id].sum=a[l],tr[id].cnt=0;
        return;
    }
    int mid=(l+r)>>1;
    build(id<<1,l,mid);
    build(id<<1|1,mid+1,r);
    pushup(id);
}

//点更新 
void update(int id,int l,int r){
    if(tr[id].cnt>=DEP)return;
    if(tr[id].l==tr[id].r){
    	tr[id].cnt++;
    	tr[id].sum=Cal(tr[id].cnt,a[tr[id].l]); 
    	return;
	}
	int mid=(tr[id].l+tr[id].r)>>1;
	if(r<=mid)update(ls(id),l,r);
	else if(l>mid)update(rs(id),l,r);
	else{
		update(ls(id),l,mid);
		update(rs(id),mid+1,r);
	}
    pushup(id);
}
 
ll query(int id,int l,int r){
    if(l<=tr[id].l&&r>=tr[id].r)
	    return tr[id].sum;
    int mid=(tr[id].l+tr[id].r)>>1;
    if(r<=mid)return query(ls(id),l,r);
    else if(l>mid)return query(rs(id),l,r);
    else {
        ll ans1=query(ls(id),l,mid);
        ll ans2=query(rs(id),mid+1,r);
        return (ans1+ans2)%P;
    }
}

void Pre_Phi(int p){
    int t=phi(p);
    Phi[DEP=0]=p;
    while (Phi[DEP]!=1){
        Phi[++DEP]=t;
        Phi[DEP]=phi(Phi[DEP-1]);
    }
    Phi[++DEP]=1;
}

int main(){
    scanf("%d %d %d %lld",&n,&m,&P,&c);
    Pre_Phi(P);
	for (int i=1;i<=n;i++) 
	   scanf("%lld",&a[i]);
    build(1,1,n);
    int opt,l,r;
    while (m--){
        scanf("%d %d %d",&opt,&l,&r);
        if (!opt) update(1,l,r);
        else printf("%lld\n",query(1,l,r));        
    }   
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_36424540/article/details/81070806
今日推荐