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

mac2022-06-30  26

题意

给一个序列,支持两个操作:将一段区间中的每一个\(a_i\)赋值为\(c^{a_i}\)\(c\) 给定;区间求和,对\(mod\)取模,不保证\(mod\)为质数

思路

显然线段树,然而此题先要单点修改

计算中指数会非常大,但是本题\(mod\)又不是质数,于是可以套用欧拉定理的推论:

\(a^{b}≡a^{b\% \varphi(p)+\varphi(p)},(mod\) \(p)\)

由于c不变,所以赋值操作进行了几次之后就会变成求:

\(c^{c^{c^{a_i}}}\% mod=\) \(c^{c^{c^{a_i}}\% \varphi(mod)+\varphi(mod)}\%mod\)

可以看出这是一个递归形式,即求

\(c^{c^{a_i}}\% \varphi(mod)\)

一直递归下去,可以(不会)证明\(log\)次左右后\(\varphi\)将为1,所以当一个点计算了\(log\)次之后,再对它进行赋值操作就不会改变它的值了

预处理出\(\varphi\),对于修改操作,直接暴力修改,当一个区间中最小的修改次数都大于了不同的\(\varphi\)的个数,就没有必要进入这个区间了

对于单点修改,直接递归求解即可,这个操作复杂度为\(log^2n\)

整个算法复杂度为线段树*单点修改,即\(O(nlog^3n)\),可能可以通过此题,可以预处理出\(c\)的幂来去掉一个\(log\)(由于指数很大,所以需要将指数拆成两部分分别预处理出c的幂,具体见代码)

Code

#include<bits/stdc++.h> #define N 50005 #define re register #define Min(x,y) ((x)<(y)?(x):(y)) using namespace std; typedef long long ll; int n,m; ll mod,phi[100],c,a[N],cnt; ll sum[N<<2],t[N<<2],pow1[N][100],pow2[N][100]; bool tag1[N][100],tag2[N][100],flag; template <class T> void read(T &x) { char c;int sign=1; while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48; while((c=getchar())>='0'&&c<='9') x=x*10+c-48; x*=sign; } ll quickpow(ll a,ll b,ll mod) { ll ret=1; while(b) { if(b&1) ret=ret*a%mod; a=a*a%mod; b>>=1; } return ret; } ll ph(ll x) { ll ret=x; for(int i=2;i*i<=x;++i) { if(x%i==0) { while(x%i==0) x/=i; ret=ret*(i-1)/i; } } if(x!=1) ret=ret*(x-1)/x; return ret; } void pushup(int rt) { sum[rt]=(sum[rt<<1]+sum[rt<<1|1])%mod; t[rt]=Min(t[rt<<1],t[rt<<1|1]); } inline ll calc(ll v,ll id) { flag=0; ll v1=v000,v2=v/10000,ret=pow1[v1][id]*pow2[v2][id]; if(ret>=phi[id]) ret=ret%phi[id],flag=1; flag|=tag1[v1][id]|tag2[v2][id]; return ret; } ll dfs(ll vi,int deep,int lim) { flag=0; if(deep==lim) { if(vi>=phi[deep]) flag=1,vi%=phi[deep]; return vi; } ll ci=dfs(vi,deep+1,lim); return calc(flag?ci+phi[deep+1]:ci,deep); } void init() { phi[cnt=0]=mod; while(phi[cnt]!=1) ++cnt,phi[cnt]=ph(phi[cnt-1]); phi[++cnt]=1; for(int i=0;i<=cnt;++i) { pow1[0][i]=1; for(int j=1;j<=10000;++j) { pow1[j][i]=pow1[j-1][i]*c; if(pow1[j][i]>=phi[i]) { pow1[j][i]%=phi[i]; tag1[j][i]=true; } tag1[j][i]|=tag1[j-1][i]; } } for(int i=0;i<=cnt;++i) { pow2[0][i]=1; tag2[1][i]=tag1[10000][i]; for(int j=1;j<=10000;++j) { pow2[j][i]=pow2[j-1][i]*pow1[10000][i]; if(pow2[j][i]>=phi[i]) { pow2[j][i]%=phi[i]; tag2[j][i]=true; } tag2[j][i]|=tag2[j-1][i]; } } } void modify(int rt,int l,int r,int x,int y) { if(t[rt]>=cnt) return; if(l==r) { ++t[rt]; sum[rt]=dfs(a[l],0,t[rt]); return; } int mid=(l+r)>>1; if(x<=mid) modify(rt<<1,l,mid,x,y); if(y>mid) modify(rt<<1|1,mid+1,r,x,y); pushup(rt); } ll query(int rt,int l,int r,int x,int y) { if(x<=l&&r<=y) return sum[rt]; int mid=(l+r)>>1; ll ret=0; if(x<=mid) ret+=query(rt<<1,l,mid,x,y); if(y>mid) ret=(ret+query(rt<<1|1,mid+1,r,x,y))%mod; return ret; } void build(int rt,int l,int r) { if(l==r) { sum[rt]=a[l]; t[rt]=0; return; } int mid=(l+r)>>1; build(rt<<1,l,mid); build(rt<<1|1,mid+1,r); pushup(rt); } int main() { // freopen("verbinden.in","r",stdin); // freopen("verbinden.out","w",stdout); read(n);read(m);read(mod);read(c); for(re int i=1;i<=n;++i) read(a[i]); init(); build(1,1,n); while(m--) { int k,l,r; read(k);read(l);read(r); if(!k) modify(1,1,n,l,r); else printf("%lld\n",query(1,1,n,l,r)); } return 0; }

转载于:https://www.cnblogs.com/Chtholly/p/11557314.html

相关资源:JAVA上百实例源码以及开源项目
最新回复(0)