P3747 [六省联考 2017] 相逢是问候
P3747 [六省联考 2017] 相逢是问候
问候出题人母亲
0x01 题意
给定一个长为\(n\)的序列,支持两个操作(\(\%p\)意义下):
- 区间修改(将该数修改为某数的该数次幂)
- 区间求和
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;
}