LOJ 2142 相逢是问候
首先 这道题目很轻易地就能想到用 线段树 求和
但是题目的难点在于 caimodp≠caimodpmodp
解决这个问题的公式是
然后这么一路推下去 我们会发现 当 ϕ...ϕ(p)=1 时 整个数字的值都不会变
(大概意思是 算了这么多次之后 就不算了)
并且根据网上其它大神的证明 这样的次数不会大于 logp
所以说暴力修改每一个点的值 然后线段树求和
时间复杂度(不严格)
O(nlog2ploga)
(最多修改 n 个点 每个点最多 logp 次修改 每次修改最多进行 logp次计算 快速幂 loga )
我们不能满足于这种有可能会T的时间复杂度
于是强行将 loga 压到常数的 超速幂 就出现了
我们发现 所有的快速幂的底数都是 c 模数都是 ϕ(p) ϕ(ϕ(p)) …
于是我们就对底数为c 模数为 ϕ(p) ϕ(ϕ(p)) …的 (1−ai) 次方项进行预处理
ai 最大是 1e8 大概是 227
所以说 我们拆分 i=a∗214+b 其中 a=imod214
因为有
然后每次求幂时 将 指数 拆分成 i=a∗214+b 然后取出 a、b 的值乘一下即可
算一下吧
ϕ(p) ϕ(ϕ(p)) …最多有 logp 个
214=16384
预处理的复杂度大概是百万级别的 相比于原来的复杂度可以说时非常小了
并且原先的复杂度降至了 O(nlog2p)
跑出来感觉非常快
#include
#include
#define mid (l+r>>1)
using namespace std;
inline int input()
{
char c=getchar();int o;bool f=0;
while(c>57||c<48)f|=(c=='-'),c=getchar();
for(o=0;c>47&&c<58;c=getchar())o=(o<<1)+(o<<3)+c-48;
return f?-o:o;
}
int n,c,mod,A[50123];
int cnt=0,phi[50];
int num[30][20000][2];
bool mark[30][20000][2];
int Gphi(int x)
{
int ans=x;
for(int i=2;i*i<=x;i++)
if(x%i==0)
{
ans=ans-ans/i;
while(x%i==0)x/=i;
}
if(x>1)ans=ans-ans/x;
return ans;
}
int KSM(int a,int b,int md,bool &mk)
{
int ans=1;
while(b)
{
if(b&1)mk|=(1LL*ans*a>=md),ans=1LL*ans*a%md;
mk|=(1LL*a*a>=md&&(b!=1));
b>>=1;
a=1LL*a*a%md;
}
return ans;
}
int FSQ(int p,int T,bool &mk)
{
T=min(T,cnt);mk=0;
mk|=mark[T][p&16383][0]|mark[T][p>>14][1];
mk|=(1LL*num[T][p>>14][1]*num[T][p&16383][0]>=phi[T]);
return 1LL*num[T][p>>14][1]*num[T][p&16383][0]%phi[T];
}
int SQ(int x,int T)
{
bool MA;
if(x>=phi[T])x=x%phi[T]+phi[T];
while(T--)
{
x=FSQ(x,T,MA);
if(MA)x+=phi[T];
}
return x%mod;
}
int L,R;
long long v[400123],done[400123];
void BUILD(int o,int l,int r)
{
if(l==r){v[o]=A[l];done[o]=0;return;}
int ls=o<<1,rs=o<<1|1;
BUILD(ls,l,mid);BUILD(rs,mid+1,r);
v[o]=(v[ls]+v[rs])%mod;
done[o]=0;
}
void CH(int o,int l,int r)
{
if(done[o]>=cnt)return;
if(l==r)
{
done[o]++;
v[o]=SQ(A[l],done[o]);
return;
}
int ls=o<<1,rs=o<<1|1;
if(L<=mid&&l<=R)CH(ls,l,mid);
if(L<=r&&mid1 ,r);
v[o]=(v[ls]+v[rs])%mod;
done[o]=min(done[ls],done[rs]);
}
int sum(int o,int l,int r)
{
if(L<=l&&r<=R)return v[o];
long long ans=0,ls=o<<1,rs=o<<1|1;
if(L<=mid&&l<=R)ans+=sum(ls,l,mid);
if(L<=r&&mid1,r);
return ans%mod;
}
void Cinit()
{
bool nmk;int phii;
for(int j,i=0;i<=cnt;i++)
{
phii=phi[i];
j=KSM(c,16384,phii,nmk);
num[i][0][0]=num[i][0][1]=1;
mark[i][0][0]=mark[i][0][1]=0;
for(int p=1;p<16384;p++)
{
num[i][p][0]=1LL*num[i][p-1][0]*c%phii;
num[i][p][1]=1LL*num[i][p-1][1]*j%phii;
mark[i][p][0]=mark[i][p-1][0]|(1LL*num[i][p-1][0]*c>=phii);
mark[i][p][1]=mark[i][p-1][1]|nmk|(1LL*num[i][p-1][1]*j>=phii);
}
}
}
int main()
{
// freopen("verbinden.in","r",stdin);
// freopen("verbinden.out","w",stdout);
n=input();int m=input();mod=phi[0]=input();c=input();
while((phi[++cnt]=Gphi(phi[cnt-1]))!=1);
phi[++cnt]=1;
Cinit();
for(int i=1;i<=n;i++)A[i]=input();
BUILD(1,1,n);
int cz;
for(int i=1;i<=m;i++)
{
cz=input();L=input();R=input();
if(cz)printf("%d\n",sum(1,1,n));
else CH(1,1,n);
}
}