链接:https://www.nowcoder.com/acm/contest/132/E
第一行两个整数 n,m 表示序列长度和操作数 接下来一行,n个整数,表示这个序列 接下来m行,可能是以下两种操作之一: 操作1:区间[l,r]加上 x 操作2:查询区间[l,r]的那个式子mod p的值
对于每个询问,输出一个数表示答案
6 4 1 2 3 4 5 6 2 1 2 10000007 2 2 3 5 1 1 4 1 2 2 4 10
1 3 1
n , m <= 500000 序列中每个数在 [1,2e9] 内,x <= 2e9 , p <= 2e7
ax mod p=ax mod ϕ(p)+(x>ϕ(p)?ϕ(p):0)mod P
我们发现对一个数取欧拉函数,log次就会变成1,而任何数模1肯定=0,所以就可以算出来了。
然而这么做还会有一些小问题。
首先我们发现后面的phi[p]这一项是可能不会加的。这个怎么办呢?
因为这个不断地幂次增长速度极快,很少几项就能够增长到远大于模数的地步。
那就先暴力的处理前面几项,然后再正常做,这样就减少了讨论次数。
但是这么搞还会存在一个问题,就是如果前几个常数次暴力处理的全是1,增长就是假的。
不过考虑到有1的话后面的全都被变成了1,所以完全没有必要担心这个,找到1就从1开始做好了。
#include
using namespace std;
const int MAX=1e6+10;
const double PI=acos(-1.0);
typedef long long ll;
int p[20000001];
ll A[500005];
inline int read()
{
int f=1,x=0;char ch;
do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
return f*x;
}
inline void phi() //欧拉函数
{
memset(p,0,sizeof p);
p[1]=1;
for(int i=2;i<=2e7;i++)
{
if(p[i])continue;
for(int j=i;j<=2e7;j+=i)
{
if(p[j]==0)p[j]=j;
p[j]=p[j]/i*(i-1);
}
}
}
inline void add(int n,int x,int y)
{
while(x<=n)
{
A[x]+=y;
x+=x&(-x);
}
}
inline ll ask(int x)
{
ll ans=0;
while(x)
{
ans+=A[x];
x-=x&(-x);
}
return ans;
}
inline int POW(ll x,ll n,int mod)
{
int res=1;
x%=mod;
while(n)
{
if(n&1)res=res*x%mod;
x=x*x%mod;
n>>=1;
}
return res;
}
inline ll cal(int x,int y,int mod)
{
if(ask(x)%mod==0)return 0;
if(mod==1)return 1;
if(x==y)return ask(x)>=mod?ask(x)%mod+mod:ask(x);
ll last,pre;
for(int i=x+1;i<=min(y,x+5);i++)
{
if(ask(i)==1||i==min(y,x+5))
{
last=ask(i);
for(int j=i-1;j>x;j--)
{
pre=last;
last=1;
while(pre--)
{
last*=ask(j);
if(last>=p[mod])return POW(ask(x),cal(x+1,y,p[mod])+p[mod],mod);
}
}
break;
}
}
return POW(ask(x),last,mod);
}
int main()
{
phi();
int n=read();
int m=read();
for(int i=1;i<=n;i++)
{
ll x=read();
add(n,i,x);
add(n,i+1,-x);
}
while(m--)
{
int op=read();
int x=read();
int y=read();
int mod=read();
if(op==1)
{
add(n,x,mod);
add(n,y+1,-mod);
}
else printf("%lld\n",cal(x,y,mod)%mod);
}
return 0;
}