【题解】 P3747 [六省联考2017]相逢是问候

线段树+扩展欧拉定理+预处理

记录一下刚了我一晚上的毒瘤题

前置知识

线段树(您都来做黑题了还不会线段树是不是有点过分)

扩展欧拉定理


扩展欧拉定理

这里丢个式子

ababmodφ(p)modp[gcd(a,p)==1]a^b\equiv a^{b \mod \varphi(p)}\mod p[gcd(a,p)==1]

ababmodp[gcd(a,p)!=1&b<φ(p)]a^b\equiv a^{b}\mod p[gcd(a,p)!=1\And b<\varphi(p)]

ababmodφ(p)+φ(p)modp[gcd(a,p)!=1&b>=φ(p)]a^b\equiv a^{b\mod \varphi(p)+\varphi(p)}\mod p[gcd(a,p)!=1\And b>=\varphi(p)]

至于该定理的证明是根据欧拉定理aφ(p)1modpa^{\varphi(p)}\equiv 1\mod p证的

有了欧拉定理扩展欧拉定理可以理解为每φ(p)\varphi(p)aa乘起来就变成了1

至于欧拉定理的证明网上应该有挺多的,扩展欧拉定理的严谨证明也有挺多的,这里就不再赘述

思路

看到题意是区间修改+区间查询和,这很线段树

但是修改操作并不与一般的线段树题相同

每一个点权值形式为cc...aic^{c^{...^{a_i}}}这样子

记这个一个k个c的幂,aia_i模p为f(ai,k,p)f(a_i,k,p)

f(k,ai)f(k,a_i)用扩展欧拉定理

(这里默认是扩展欧拉定理的第三种情况,但具体实现时需考虑其他的情况)

f(ai,k,p)cf(ai,k1,φ(p))+φ(p)modpf(a_i,k,p)\equiv c^{f(a_i,k-1, \varphi(p))+\varphi(p)}\mod p

我们可以对下面这个东西做同样的事情

f(ai,k1,φ(p))f(a_i,k-1,\varphi(p))

模数会有p,φ(p),φ(φ(p))...p,\varphi(p),\varphi(\varphi(p))...

直到某次迭代得到的值为1则后面的幂都没有意义了

由于偶数的欧拉函数迭代1次后至少缩小一半(所有偶数不与它互质)

奇数的欧拉函数迭代1次后必为偶数(由φ\varphi的计算式可知)

故迭代次数是log级的

这也代表着一个值最多经过log个修改操作后值不再改变

都会变成ccc...cc^{c^{c^{...^c}}}

记对pp进行hh次欧拉函数迭代后p变为1

我们只要对于每一个aia_i预处理f(i,ai,m),i[0,h]f(i,a_i,m),i\in[0,h]m为p迭代出来的所有数即可

而想求f,我们可以设s1(i,m),s2(i,m)s_1(i,m),s_2(i,m)用于求cic^i模m的值,m的定义与上方一样

而特判欧拉定理我们可以再开两个bool数组判断

具体可以看下代码里的pre函数

复杂度分析

每次修改对于每一个位置暴力修改,由于一个位置修改log次后继续修改值是不变的

故我们记录一下每个位置的修改次数,每个点最多修改log次。

由于我们每次遍历了所有覆盖某个区间的线段树节点,总复杂度O(nlog2)O(nlog^2)

我们预处理出了f(i,j,k)f(i,j,k),这个东西是nlog2nlog^2的

空间复杂度也是O(nlog2)O(nlog^2)

好像题解大佬有空间更优秀的做法qaq

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=50005;
struct seg{
	int v,mn;
}t[maxn<<2];
int n,m,p,c;
int len=0;
int phi[30];
long long s1[10005][30],s2[10005][30];
bool b1[maxn][30],b2[maxn][30];
long long f[maxn][30][30];
bool bj[maxn][30][30];
int g[30];
int a[maxn];
int read(){
	int x=0,y=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')y=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*y;
}
int gcd(register int x,register int y){
	return y==0?x:gcd(y,x%y);
}
int get_phi(int x){
	long long ans=x;
	for(long long i=2;i<=sqrt(x);i++){
		if(x%i)continue;
		ans=ans*(i-1)/i;
		while(x%i==0)x/=i;
	}
	if(x>1)ans=ans*(x-1)/x;
	return ans;
}
void pre(){
	int x=p;
	phi[0]=p;
	while(x!=1){x=get_phi(x);phi[++len]=x;}
	phi[++len]=1;
	for(int i=0;i<=len;i++)g[i]=gcd(c,phi[i]);
	for(int j=0;j<=len;j++){
		s2[0][j]=1;
		for(int i=1;i<=10000;i++){
			s2[i][j]=s2[i-1][j]*c;
			if(s2[i][j]>=phi[j]){s2[i][j]%=phi[j];b2[i][j]=1;}
			b2[i][j]|=b2[i-1][j];
		}
	}
	for(int j=0;j<=len;j++){
		s1[0][j]=1;
		b1[1][j]=b2[10000][j];
		for(int i=1;i<=10000;i++){
			s1[i][j]=s1[i-1][j]*s2[10000][j];
			if(s1[i][j]>=phi[j]){s1[i][j]%=phi[j];b1[i][j]=1;}
			b1[i][j]|=b1[i-1][j];
		}
	}
	for(int i=1;i<=n;i++){
		for(int k=0;k<=len;k++){
			f[i][0][k]=a[i]%phi[k];
			if(a[i]>=phi[k])bj[i][0][k]=1;
		}
		for(int j=1;j<=len;j++){
			f[i][j][len]=0;
			for(int k=0;k<len;k++){
				f[i][j][k]=s1[f[i][j-1][k+1]/10000][k]*s2[f[i][j-1][k+1]%10000][k];
				bj[i][j][k]=(b1[f[i][j-1][k+1]/10000][k]||b2[f[i][j-1][k+1]%10000][k]);
				if(f[i][j][k]>=phi[k]){f[i][j][k]%=phi[k];bj[i][j][k]=1;}
				if(g[k]!=1&&bj[i][j-1][k+1]){
					f[i][j][k]=f[i][j][k]*s1[phi[k+1]/10000][k]%phi[k]*s2[phi[k+1]%10000][k];
					if(f[i][j][k]>=phi[k]){f[i][j][k]%=phi[k];bj[i][j][k]=1;}
					bj[i][j][k]=(bj[i][j][k]||b1[phi[k+1]/10000][k]||b2[phi[k+1]%10000][k]);
				}
			}	
		}
	}
	return ;
}
void pushup(int k){
	t[k].v=t[k<<1].v+t[k<<1|1].v;
	t[k].mn=min(t[k<<1].mn,t[k<<1|1].mn);
	return ;
}
void build(int k,int l,int r){
	if(l==r){
		t[k].v=a[l];
		t[k].mn=0;
		return ;
	}
	int mid=l+((r-l)>>1);
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	pushup(k);
	return ;
}
void modify(int k,int l,int r,int x,int y){
	if(t[k].mn>=len)return ;
	if(l>y||r<x)return ;
	if(l==r){
		t[k].mn++;t[k].v=f[l][t[k].mn][0]%p;
		return ;
	}
	int mid=l+((r-l)>>1);
	if(t[k<<1].mn<len)modify(k<<1,l,mid,x,y);
	if(t[k<<1|1].mn<len)modify(k<<1|1,mid+1,r,x,y);
	pushup(k);
	return ;
}
int query(int k,int l,int r,int x,int y){
	if(l>y||r<x)return 0;
	if(l>=x&&r<=y)return t[k].v%p;
	int mid=l+((r-l)>>1);
	return (query(k<<1,l,mid,x,y)+query(k<<1|1,mid+1,r,x,y))%p;
}
signed main(){
	n=read();m=read();p=read();c=read();
	for(int i=1;i<=n;i++)a[i]=read();
	pre();
	build(1,1,n);
	for(int i=1;i<=m;i++){
		int opt,l,r;
		opt=read();l=read();r=read();
		if(opt==0)modify(1,1,n,l,r);
		else printf("%lld\n",query(1,1,n,l,r));
	}
	return 0;
}