zoukankan      html  css  js  c++  java
  • 【bzoj4869】[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

    Sol

    根据扩展欧拉定理,参照bzoj3884,我们发现某个数字做一定次数比操作之后就不会变了,这个次数在(logn)左右,所以就可以用线段树维护区间和以及这个区间的数字有没有都处理完毕,然后直接维护即可。时间复杂度(nlog^3n),由于本题数据范围(50000),所以可以通过。

    注意细节:判断某个数字有没有超过(varphi(p)),以及(varphi(1)=varphi(2)=1),但是我们必须要在(p=1)的时候才能停止。

    Code

    #include <bits/stdc++.h>
    using namespace std;
    int n,m,P,c,K,op,l,r,a[50005],p[50005],V[50005],pr[50005],tot,sm[200005],mn[200005];
    int phi(int x)
    {
    	int res=x;
    	for(int i=1;pr[i]*pr[i]<=x;i++)
    	{
    		if(x%pr[i]) continue;
    		res-=res/pr[i];
    		while(x%pr[i]==0) x/=pr[i];
    	}
    	if(x>1) res-=res/x;return res;
    }
    void build(int x,int l,int r)
    {
    	if(l==r){scanf("%d",&a[l]);sm[x]=a[l]%P;mn[x]=0;return;}
    	int M=(l+r)>>1;build(x<<1,l,M);build(x<<1|1,M+1,r);
    	sm[x]=(sm[x<<1]+sm[x<<1|1])%P;mn[x]=min(mn[x<<1],mn[x<<1|1]);
    }
    int ksm(int a,int b,int P,bool &f)
    {
    	int res=1;bool gg=0;
    	for(;b;b>>=1,a=1ll*a*a%P)
    	{
    		if(b&1) f|=(gg|(1ll*res*a>=P)),res=1ll*res*a%P;
    		if(1ll*a*a>=P) gg=1;
    	}
    	return res;
    }
    int cal(int dep,int x)
    {
    	int res=x;if(res>=p[dep]) res=res%p[dep]+p[dep];
    	while(dep)
    	{
    		dep--;bool flag=0;
    		res=ksm(c,res,p[dep],flag);
    		if(flag) res+=p[dep];
    	}
    	return res%p[dep];
    }
    void upd(int x,int l,int r,int b,int e)
    {
    	if(mn[x]>=K) return;
    	if(l==r){mn[x]++;sm[x]=cal(mn[x],a[l]);return;}
    	int M=(l+r)>>1;
    	if(b<=M) upd(x*2,l,M,b,e);if(e>M) upd(x*2+1,M+1,r,b,e);
    	sm[x]=(sm[x<<1]+sm[x<<1|1])%P;mn[x]=min(mn[x<<1],mn[x<<1|1]);
    }
    int que(int x,int l,int r,int b,int e)
    {
    	if(b<=l&&r<=e) return sm[x];
    	int M=(l+r)>>1;
    	return ((b<=M?que(x*2,l,M,b,e):0)+(e>M?que(x*2+1,M+1,r,b,e):0))%P;
    }
    int main()
    {
    	for(int i=2;i<=50000;i++)
    	{
    		if(!V[i]) pr[++tot]=i;
    		for(int j=1;j<=tot&&i*pr[j]<=50000;j++){V[i*pr[j]]=1;if(i%pr[j]==0) break;}
    	}
    	scanf("%d%d%d%d",&n,&m,&P,&c);
    	p[0]=P;while(p[K]!=1){++K;p[K]=phi(p[K-1]);}p[++K]=1;
    	for(build(1,1,n);m--;)
    	{
    		scanf("%d%d%d",&op,&l,&r);
    		if(op==0) upd(1,1,n,l,r);
    		else printf("%d
    ",que(1,1,n,l,r));
    	}
    }
    
  • 相关阅读:
    面向对象编程(OOP)、面向组件编程(COP)、面向方面编程(AOP)和面向服务编程(SOP)
    编程思想、软件工程思想、模块内变化时对其他模块的影响
    读林锐《软件工程思想》------讲述软件开发和做程序员的道理
    编程思想的理解(POP,OOP,SOA,AOP)
    javascript alert乱码的解决方法
    curl说明
    手机号正则表达式精简
    织梦自定义表单 在线报名 在线预约听语音
    Html Mailto标签详细使用方法
    织梦DedeCMS提示信息框的修改,修改ShowMsg方法函数
  • 原文地址:https://www.cnblogs.com/CK6100LGEV2/p/9419411.html
Copyright © 2011-2022 走看看