题解:
看过去一眼:线段树?暴力?然后就不知道该干什么了。
看题解:woc?这公式?(好吧不会这公式就做不成题了…)
欧拉定理EXT: a b ≡ a b m o d    ϕ ( p ) + ϕ ( p ) ( m o d p ) , b ≥ ϕ ( p ) a^b\equiv a^{b\mod \phi(p)+\phi(p)} (mod\ p),b\ge \phi(p) ab≡abmodϕ(p)+ϕ(p)(mod p),b≥ϕ(p)
在这里就别想每次对p取模之后求幂了,样例就是个不错的反例。
那么我们可以证明最多暴力修改
O
(
log
p
)
O(\log p)
O(logp)次值就不会改变了。
还有最后求结果要求到
ϕ
(
1
)
=
1
\phi(1)=1
ϕ(1)=1,不能求到
ϕ
(
2
)
=
1
\phi(2)=1
ϕ(2)=1就算完了。
我觉得这篇讲得挺详细的qwq。
然后我们就可以用线段树存一个操作次数,操作到了
ϕ
(
1
)
=
1
\phi(1)=1
ϕ(1)=1的次数就不用维护了。前一份代码的calc确实很醚…但能过。后面代码是修了一下锅的。
由于做的时候并不敢保证卡常,所以优化了一下。
每次快速幂都要花点时间,干脆预处理好了。
参考了这位dalao的思路,预处理一下
[
0
,
10000
]
[0,10000]
[0,10000]中每一个数的每一个
ϕ
\phi
ϕ的次方,然后再处理这些数的10000次方,砍掉一个log。
代码:
#include<cstdio>
#include<cmath>
#define maxn 50005
#define maxm 105
int n,m,p,c,pcnt,phi[maxm],p1[maxm][maxn],p2[maxm][maxn],a[maxn];
struct node { int val,sum,lazy; } tree[maxn*4];
int powmod(int a,int n,int mod)
{
int res=1;
while(n)
{
if(n&1) res=1ll*res*a%mod;
a=1ll*a*a%mod;
n>>=1;
}
return res;
}
int Get(int x)
{
int res=x,tmp=x;
for(int i=2;i*i<=x;i++)
if(tmp%i==0)
{
res=1ll*res*(i-1)/i;
while(tmp%i==0) tmp/=i;
}
if(tmp>1) res=1ll*res*(tmp-1)/tmp;
return res;
}
void init()
{
phi[0]=p;
while(phi[pcnt]>1) pcnt++,phi[pcnt]=Get(phi[pcnt-1]);
phi[++pcnt]=1;
for(int i=0;i<=pcnt;i++)
{
for(int j=0;j<=10000;j++) p1[i][j]=powmod(c,j,phi[i]);
for(int j=0;j<=10000;j++) p2[i][j]=powmod(p1[i][10000],j,phi[i]);
}
}
void Build(int i,int l,int r)
{
if(l==r) { tree[i].val=0,tree[i].sum=a[l]; return; }
int mid=(l+r)>>1;
Build(i<<1,l,mid); Build(i<<1|1,mid+1,r);
tree[i].sum=(tree[i<<1].sum+tree[i<<1|1].sum)%p;
}
int Pow(int px,int n) { return 1ll*p2[px][n/10000]*p1[px][n%10000]%phi[px]; }
int calc(int x,int cnt)
{
int res=x;
for(int i=cnt;i>=1;i--)
{
if(res>=phi[i]) res=res%phi[i]+phi[i];
res=Pow(i-1,res);
if(!res) res=phi[i-1];
}
return res;
}
void Modify(int i,int l,int r,int ql,int qr)
{
if(tree[i].lazy) return;
if(l==r)
{
tree[i].sum=calc(a[l],++tree[i].val);
if(tree[i].val==pcnt) tree[i].lazy=true;
return;
}
int mid=(l+r)>>1;
if(ql<=mid) Modify(i<<1,l,mid,ql,qr);
if(qr>mid) Modify(i<<1|1,mid+1,r,ql,qr);
tree[i].sum=(tree[i<<1].sum+tree[i<<1|1].sum)%p,tree[i].lazy=tree[i<<1].lazy&tree[i<<1|1].lazy;
}
int Query(int i,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr) return tree[i].sum;
int mid=(l+r)>>1;
int res=0;
if(ql<=mid) res=(res+Query(i<<1,l,mid,ql,qr))%p;
if(qr>mid) res=(res+Query(i<<1|1,mid+1,r,ql,qr))%p;
return res;
}
void Debug(int i,int l,int r)
{
if(l==r) { printf("%d ",tree[i].sum); return; }
int mid=(l+r)>>1;
Debug(i<<1,l,mid); Debug(i<<1|1,mid+1,r);
}
int main()
{
scanf("%d%d%d%d",&n,&m,&p,&c);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
init();
Build(1,1,n);
while(m--)
{
int op,l,r;
scanf("%d%d%d",&op,&l,&r);
if(!op) Modify(1,1,n,l,r);
else printf("%d\n",Query(1,1,n,l,r));
}
}
Upd 2019 03 01: 修了点锅…总觉得上面代码不太严谨所以趁着老师把这道题拿出来讲的机会又搞了一遍…
#include<cstdio>
#include<cmath>
#define maxn 50005
#define maxm 105
#define N 16384
int n,m,p,c,pcnt,phi[maxm],p1[maxm][maxn],p2[maxm][maxn],a[maxn];
bool ok1[maxm][maxn],ok2[maxm][maxn];
struct node { int val,sum,lazy; } tree[maxn*4];
int powmod(int a,int n,int mod,bool &ok)
{
int res=1,ok1=0; ok=0;
while(n)
{
if(n&1) ok|=(1ll*res*a>=mod)|ok1,res=1ll*res*a%mod;
ok1|=(1ll*a*a>=mod),a=1ll*a*a%mod;
n>>=1;
}
return res;
}
int Get(int x)
{
int res=x,tmp=x;
for(int i=2;i*i<=x;i++)
if(tmp%i==0)
{
res=1ll*res*(i-1)/i;
while(tmp%i==0) tmp/=i;
}
if(tmp>1) res=1ll*res*(tmp-1)/tmp;
return res;
}
void init()
{
phi[0]=p;
while(phi[pcnt]>1) pcnt++,phi[pcnt]=Get(phi[pcnt-1]);
phi[++pcnt]=1;
for(int i=0;i<=pcnt;i++)
{
for(int j=0;j<=N;j++) p1[i][j]=powmod(c,j,phi[i],ok1[i][j]);
for(int j=0;j<=N;j++) p2[i][j]=powmod(p1[i][N],j,phi[i],ok2[i][j]);
}
}
void Build(int i,int l,int r)
{
if(l==r) { tree[i].val=0,tree[i].sum=a[l]; return; }
int mid=(l+r)>>1;
Build(i<<1,l,mid); Build(i<<1|1,mid+1,r);
tree[i].sum=(tree[i<<1].sum+tree[i<<1|1].sum)%p;
}
int Pow(int px,int n,bool &ok)
{
ok=ok2[px][n/N]|ok1[px][n%N]|(1ll*p2[px][n/N]*p1[px][n%N]>=phi[px]);
return 1ll*p2[px][n/N]*p1[px][n%N]%phi[px];
}
int calc(int x,int cnt)
{
int res=x; bool ok;
if(res>=phi[cnt]) res=res%phi[cnt]+phi[cnt];
for(int i=cnt;i>=1;i--)
{
res=Pow(i-1,res,ok);
if(ok) res+=phi[i-1];
}
return res%phi[0];
}
void Modify(int i,int l,int r,int ql,int qr)
{
if(tree[i].lazy) return;
if(l==r)
{
tree[i].sum=calc(a[l],++tree[i].val);
if(tree[i].val==pcnt) tree[i].lazy=true;
return;
}
int mid=(l+r)>>1;
if(ql<=mid) Modify(i<<1,l,mid,ql,qr);
if(qr>mid) Modify(i<<1|1,mid+1,r,ql,qr);
tree[i].sum=(tree[i<<1].sum+tree[i<<1|1].sum)%p,tree[i].lazy=tree[i<<1].lazy&tree[i<<1|1].lazy;
}
int Query(int i,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr) return tree[i].sum;
int mid=(l+r)>>1;
int res=0;
if(ql<=mid) res=(res+Query(i<<1,l,mid,ql,qr))%p;
if(qr>mid) res=(res+Query(i<<1|1,mid+1,r,ql,qr))%p;
return res;
}
int main()
{
scanf("%d%d%d%d",&n,&m,&p,&c);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
init();
Build(1,1,n);
while(m--)
{
int op,l,r;
scanf("%d%d%d",&op,&l,&r);
if(!op) Modify(1,1,n,l,r);
else printf("%d\n",Query(1,1,n,l,r));
}
}