Jzoj5636 Power

227 篇文章 3 订阅
105 篇文章 0 订阅
该博客介绍了如何处理区间查询时,当模数不是质数的情况。通过利用指数循环节的性质x^k=x^(k%phi(M)+phi(M))%M(k>=phi(M)),可以简化计算。博主提到,由于欧拉函数的迭代在logn次后会收敛到1,因此可以递归求解。文章中还提到,需要预先使用线性筛计算10^7以内的欧拉函数值,并对大于此值的数分解质因数。在处理询问时,要注意可能存在answer[l+1,r]小于phi(M)的情况,此时不能直接应用指数循环节的结论。判断答案是否超过模数的方法是对比带模运算和不带模运算的结果是否一致。" 79188768,5700666,深入解析:IO复用模式——select、poll与epoll,"['IO复用', 'epoll', '网络编程', 'Linux内核', 'NIO']
摘要由CSDN通过智能技术生成

近期没有写过博客感觉要死了->重返jz深造

一道区间查询的问题,因为模数不是质数,我们考虑利用指数循环节这个东西:

x^k=x^(k%phi(M)+phi(M))%M 这里要求k>=phi(M)

那么可以写成Ans[l,r]%M=x^(Ans[l+1,r]%phi(M))%M

由于phi的迭代会在logn次收敛为1,所以我们可以直接递归来做

先用线性筛筛出10^7以内的phi值,大过这个的就分解质因数来计算

注意一个问题,对于一个询问[l,r],有可能answer[l+1,r]并不能达到phi(M),这个时候就不能使用上面的结论

至于如何判断一个区间的答案是否大过模数,可以做一次不带取模的pow和一次带取模的,如果两次答案不同就说明答案大过模数

#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 10000000
#define LL long long
using namespace std;
int n,m,M,s[100010]; bool vis[N+10];
int t=0,w[N>>2],phi[N+10];
inline LL bow(LL x,LL k,LL s=1){
	for(;k;x=x*x,k>>=1) k&1?s=s*x:0;
	return s;
}
inline LL pow(LL x,LL k,LL M,LL s=1){
	for(;k;x=x*x%M,k>>=1) k&1?s=s*x%M:0;
	return s;
}
inline int cphi(int M){
	int T=M;
	for(int i=1;w[i]*w[i]<=M;++i)
		if(M%w[i]==0) for(T=T/w[i]*(w[i]-1);M%w[i]==0;M/=w[i]);
	if(M>1) T=T/M*(M-1);
	return T;
}
inline bool gcd(int l,int r,int M,int& S){
	if(M==1){ S=0; return 1; }
	if(l==r){ S=s[l]%M; return s[l]>=M; }
	int ph=(M<=N?phi[M]:cphi(M));
	if(gcd(l+1,r,ph,S)){
		S=pow(s[l],ph+S,M);
		return s[l]>=1;
	} else {
		int T=bow(s[l],S);
		S=pow(s[l],S,M);
		return S!=T;
	}
}
int main(){
	freopen("power.in","r",stdin);
	freopen("power.out","w",stdout);
	scanf("%d%d",&n,&M);
	for(int i=1;i<=n;++i) scanf("%d",s+i);
	for(int i=2;i<=N;++i){
		if(!vis[i]) phi[w[++t]=i]=i-1;
		for(int j=1;j<=t && i*w[j]<=N;++j){
			vis[i*w[j]]=1;
			if(i%w[j]==0){ phi[i*w[j]]=phi[i]*w[j]; break; }
			phi[i*w[j]]=phi[i]*(w[j]-1);
		}
	}
	scanf("%d",&m);
	for(int l,r;m--;){
		scanf("%d%d",&l,&r);
		gcd(l,r,M,r);
		printf("%d\n",r);
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值