背景:
寫一下這一週集訓做的題目吧。
其實是切不動題了。
題目傳送門:
https://www.luogu.org/problem/P3747
題意:
一個序列,兩種操作。
將變爲;
求。
思路:
類似的題目詳見:。
同樣的我們需要解決的問題。
我們依然可以考慮歐拉降冪。
那麼同樣的來做就可以了。
對於區間和的求解用線段樹即可。
特別地,如果當前區間的最小修改次數已經到了的修改爲的次數,直接結束即可,因爲此時的指數已經爲了。
可是容易算得時間複雜度爲:,級別直接爆炸,分數爲。
不妨光速冪即可。
注意,應取,而需要指數加的部分用個數組存下來即可。
代碼:
#pragma GCC optimize("Ofast")
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define LL long long
#define I inline
#define R register
using namespace std;
int n,m,p,c,limit,len=0,block;
int a[50010],phi[50];
LL POW1[50][20010],POW2[50][20010];
bool flag1[50][20010],flag2[50][20010];
struct node{int l,r,lc,rc,d,cnt,sum;} tr[400010];
I int get_phi(int x)
{
int t=x,tot=x;
for(R int i=2;i*i<=t;i++)
if(!(x%i))
{
tot=tot/i*(i-1);
while(!(x%i)) x/=i;
}
return x>1?tot/x*(x-1):tot;
}
void init()
{
phi[limit=0]=p;
while(phi[limit]>1) limit++,phi[limit]=get_phi(phi[limit-1]);
phi[++limit]=1;
block=2*(sqrt(p)+1);
for(int i=0;i<=limit;i++)
{
POW1[i][0]=1;
for(int j=1;j<=block;j++)
{
POW1[i][j]=POW1[i][j-1]*c;
if(POW1[i][j]>=phi[i]) POW1[i][j]%=phi[i],flag1[i][j]=true;
flag1[i][j]|=flag1[i][j-1];
}
}
for(int i=0;i<=limit;i++)
{
POW2[i][0]=1;
for(int j=1;j<=block;j++)
{
POW2[i][j]=POW2[i][j-1]*POW1[i][block];
if(POW2[i][j]>=phi[i]) POW2[i][j]%=phi[i],flag2[i][j]=true;
flag2[i][j]|=flag2[i][j-1];
}
}
}
void build(int l,int r)
{
int now=++len;
tr[now]=(node){l,r,-1,-1,0,0,0};
if(l==r) tr[now].sum=a[l]%p;
if(l<r)
{
int mid=(l+r)>>1;
tr[now].lc=len+1; build(l,mid);
tr[now].rc=len+1; build(mid+1,r);
tr[now].sum=((LL)tr[tr[now].lc].sum+tr[tr[now].rc].sum)%p;
}
}
bool flag;
int get_POW(int x,int id)
{
int t1=x%block,t2=x/block;
LL tot=POW2[id][t2]*POW1[id][t1];
flag|=flag2[id][t2]|flag1[id][t1];
if(tot>=phi[id]) tot%=phi[id],flag=true;
return tot;
}
I int work(int tot,int t)
{
if(tot>phi[t]) tot=tot%phi[t]+phi[t];
for(R int i=t;i>=1;i--)
{
flag=false;
tot=get_POW(tot,i-1);
if(flag) tot+=phi[i-1];
}
return tot;
}
void change(int now,int l,int r)
{
if(tr[now].cnt>=limit) return;
if(tr[now].l==tr[now].r) {tr[now].sum=work(a[tr[now].l],++tr[now].cnt);return;}
int lc=tr[now].lc,rc=tr[now].rc,mid=(tr[now].l+tr[now].r)>>1;
if(r<=mid) change(lc,l,r);
else if(l>mid) change(rc,l,r);
else change(lc,l,mid),change(rc,mid+1,r);
tr[now].cnt=min(tr[lc].cnt,tr[rc].cnt);
tr[now].sum=((LL)tr[lc].sum+tr[rc].sum)%p;
}
int findsum(int now,int l,int r)
{
if(tr[now].l==l&&tr[now].r==r) return tr[now].sum%p;
int lc=tr[now].lc,rc=tr[now].rc,mid=(tr[now].l+tr[now].r)>>1;
if(r<=mid) return findsum(lc,l,r);
else if(l>mid) return findsum(rc,l,r);
else return ((LL)findsum(lc,l,mid)+findsum(rc,mid+1,r))%p;
}
I char getc()
{
static char buf[1<<20],*fs,*ft;
return(fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<20,stdin)),fs==ft)?EOF:*fs++;
}
I void read(int &x)
{
char ch=getc();
for(x=0;ch=='\n'||ch==' ';ch=getc());
for(;ch!='\n'&&ch!=' '&&ch!=EOF;ch=getc())
x=((x+(x<<2))<<1)+(ch^0x30);
}
int main()
{
int t,x,y;
read(n),read(m),read(p),read(c);
init();
for(R int i=1;i<=n;i++)
read(a[i]);
build(1,n);
for(R int i=1;i<=m;i++)
{
read(t),read(x),read(y);
if(x>y) swap(x,y);
if(!t) change(1,x,y); else printf("%d\n",findsum(1,x,y));
}
}