P3747 [六省联考 2017] 相逢是问候

P3747 [六省联考 2017] 相逢是问候

问候出题人母亲

0x01 题意

给定一个长为\(n\)的序列,支持两个操作(\(\%p\)意义下):

  1. 区间修改(将该数修改为某数的该数次幂)
  2. 区间求和

0x02 解

看见区间求和就是线段树

区间修改应该和数学有关

求幂的话可以用欧拉定理

然后这个题应该就做完了

哦对还有预处理不然会T

不搞了不是强迫症

线段树维护两个参数,一个是这个区间的和,一个是这个区间里求幂次数的最小值

当求幂次数大于\(log(p)\)时模数就变成\(1\)了,就没有必要算下去了

因此每个数都最多算\(log\)次,复杂度是\(O(nlog^2)\)

单点暴力改就行不用tag,tag还麻烦

预处理要把每个数的幂提前算好

用扩展欧拉定理算

开俩数组把\(c\)的幂装下,这里用到压位的思想,把前四个数用一个数组表示,后四个数用一个数组表示,然后再开俩数组存它们可不可以用欧拉定理

最后递推合并成一个算形如\(c^{c^{c^{c...}}}\)的三维数组就行了

具体看代码

0x03 码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=100010;

int phi[N],g[N],s1[N][30],s2[N][30];
bool b1[N][30],b2[N][30];
int f[N][30][30];
bool bj[N][30][30];
int a[N],len=0,n,m,p,c;

struct node{
	int mn,s;
}t[N<<2];

int gcd(int a,int b){
    return b?a:gcd(b,a%b);
}

int getphi(int n){
	int ans=n,m=sqrt(n);
	for(int i=2;i<=m;i++){
		if(n%i==0){
			ans=ans/i*(i-1);
			while(n%i==0) n/=i;
		}
	}
	if(n>1) ans=ans/n*(n-1);
	return ans;
}

void pre(){
	int x=p;
	phi[0]=p;
	while(x!=1) x=getphi(x),phi[++len]=x;
	phi[++len]=1;
	for(int i=0;i<=len;i++) g[i]=gcd(c,phi[i]);
	for(int i=0;i<=len;i++){
		s2[0][i]=1;
		for(int j=1;j<=10000;j++){
			s2[j][i]=s2[j-1][i]*c;
			if(s2[j][i]>=phi[i]) s2[j][i]%=phi[i],b2[j][i]=1;
			b2[j][i]|=b2[j-1][i];
		}
	}
	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 x){
	t[x].mn=min(t[x<<1].mn,t[x<<1|1].mn);
	t[x].s=t[x<<1].s+t[x<<1|1].s;
	return;
}

void build(int x,int l,int r){
	if(l==r){
		t[x].s=a[l];
		t[x].mn=0;
		return;
	}
	int mid=(l+r)>>1;
	build(x<<1,l,mid);
	build(x<<1|1,mid+1,r);
	pushup(x);
}

void change(int x,int l,int r,int cl,int cr){
	if(t[x].mn>=len) return;
	if(l>cr||r<cl) return;
	if(l==r){
		t[x].mn++;
		t[x].s=f[l][t[x].mn][0]%p;
		return;
	}
	int mid=(l+r)>>1;
	if(t[x<<1].mn<len) change(x<<1,l,mid,cl,cr);
	if(t[x<<1|1].mn<len) change(x<<1|1,mid+1,r,cl,cr);
	pushup(x);
	return;
}

int query(int x,int l,int r,int ql,int qr){
	if(l>qr||r<ql) return 0;
	if(l>=ql&&r<=qr) return t[x].s%p;
	int mid=(l+r)>>1;
	return (query(x<<1,l,mid,ql,qr)+query(x<<1|1,mid+1,r,ql,qr))%p;
}

signed main(){
	cin>>n>>m>>p>>c;
	for(int i=1;i<=n;i++) cin>>a[i];
	pre(); 
	build(1,1,n);
	for(int i=1;i<=m;i++){
		int op,l,r;
		cin>>op>>l>>r;
		if(op) printf("%lld\n",query(1,1,n,l,r));
		else change(1,1,n,l,r);
	}
	
	return 0;
}
posted @ 2021-03-09 11:08  wsy_jim  阅读(220)  评论(0编辑  收藏  举报