看到區間修改區間查詢,大家一定會覺得這是一個線段樹題
然後再看修改操作
這玩意真的能用線段樹維護嗎???
答案是:顯然不能
那怎麼辦呢?
看到這麼多落在一起的冪,好多還都一樣(),我們可以聯想到這道題所以我們可以用擴展歐拉定理推一下,順便給自己的博客打廣告嚶嚶嚶~
根據擴展歐拉定理:
那麼經過一段時間,當之後,我們的式子就變成了,就和無關了!!!
於是我們又想到了這道題,採取一樣的做法,對於修改,暴力修改如果修改到之後就不管他了
可以證明,最多經過次之後,,也就是說每個點最多會被修改次,而每次修改最多需要次遞歸,每次遞歸的時候需要的時間來算快速冪,也就是說這道題我們就做出了一種的做法
然後我們手推一下就會發現顯然是跑不過去的,但是能夠拿到分,但是因爲這題標程出鍋了,所以數據特別水,所以實際能夠拿到。
但是注意這裏有一個細節,因爲擴展歐拉定理只有在指數的時候才成立,所以我們要判斷一下要不要加上,這裏可以用一個flag變量來求
先貼一個分代碼吧
#include <bits/stdc++.h>
using namespace std;
# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)
typedef long long ll;
const int N=1e5+5;
template<typename T> void read(T &x){
x=0;int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
x*=f;
}
# define int long long
int n,m,p,c;
int a[N];
int phi[N],dep;
bool flag;
struct segment_tree{
int l,r;
int sum,tim;
}seg[N<<2];
# define lc (u<<1)
# define rc (u<<1|1)
int getphi(int x){
int res=x;
for(int i=2;1ll*i*i<=x;i++){
if(x%i==0)
res=res/i*(i-1);
while(x%i==0)x/=i;
}
if(x>1)res=res/x*(x-1);
return res;
}
void init(){
int x=p;
phi[0]=x;
while(x>1){
x=getphi(x);
phi[++dep]=x;
}
phi[++dep]=1;
}
int Qpow(int base,int ind,int p){
int res=1;
while(ind){
if(ind&1)res=1ll*res*base;
base=1ll*base*base%p;
ind>>=1;
if(res>=p)flag=true,res%=p;
if(base>=p)flag=true,base%=p;
}
return res;
}
int calc(int id,int lim,int d){
flag=false;
if(d==lim){
if(a[id]>=phi[d]){
flag=true;
return a[id]%phi[d];
}
return a[id];
}
int x=calc(id,lim,d+1);
if(flag)x+=phi[d+1],flag=false;
return Qpow(c,x,phi[d]);
}
void pushup(int u){
seg[u].sum=(seg[lc].sum+seg[rc].sum)%p;
seg[u].tim=min(seg[lc].tim,seg[rc].tim);
}
void build(int u,int l,int r){
seg[u].l=l,seg[u].r=r;
if(l==r){
seg[u].sum=a[l];
seg[u].tim=0;
return;
}
int mid=l+r>>1;
build(lc,l,mid);
build(rc,mid+1,r);
pushup(u);
}
void update(int u,int l,int r){
if(seg[u].tim>=dep)return;
if(seg[u].l==seg[u].r){
seg[u].tim++;
seg[u].sum=calc(seg[u].l,seg[u].tim,0);
return;
}
int mid=seg[u].l+seg[u].r>>1;
if(l<=mid)update(lc,l,r);
if(r>mid)update(rc,l,r);
pushup(u);
}
int query(int u,int l,int r){
if(seg[u].l>=l&&seg[u].r<=r)return seg[u].sum;
int mid=seg[u].l+seg[u].r>>1;
int res=0;
if(l<=mid)res+=query(lc,l,r);
if(r>mid)res+=query(rc,l,r);
res%=p;
return res;
}
signed main()
{
read(n),read(m),read(p),read(c);
Rep(i,1,n)read(a[i]);
init();
build(1,1,n);
Rep(i,1,m){
int opt,x,y;
read(opt),read(x),read(y);
if(!opt)update(1,x,y);
else printf("%lld\n",query(1,x,y));
}
return 0;
}
那麼我們考慮怎麼優化
我們發現我們每次暴力求答案的時候,底數是一樣的,而模數也不多
所以我們可以對於每個模數進行一下光速冪(繼續打廣告)然後我們可以優化掉一個變成就解決了
同時我們也要在預處理的時候處理一下有沒有溢出的情況(在做的過程中出現了),如果有就加上
#include <bits/stdc++.h>
using namespace std;
# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)
typedef long long ll;
const int N=1e5+5;
const int bl=10000;
const int M=65;
template<typename T> void read(T &x){
x=0;int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
x*=f;
}
# define int long long
int n,m,p,c;
int a[N];
int phi[N],dep;
int qpow[bl+5][M][2];
bool flag,over[bl+5][M][2];
struct segment_tree{
int l,r;
int sum,tim;
}seg[N<<2];
# define lc (u<<1)
# define rc (u<<1|1)
int getphi(int x){
int res=x;
for(int i=2;1ll*i*i<=x;i++){
if(x%i==0)
res=res/i*(i-1);
while(x%i==0)x/=i;
}
if(x>1)res=res/x*(x-1);
return res;
}
void init(){
int x=p;
phi[0]=x;
while(x>1){
x=getphi(x);
phi[++dep]=x;
}
phi[++dep]=1;
Rep(i,0,dep){
qpow[0][i][0]=1;
Rep(j,1,bl){
qpow[j][i][0]=qpow[j-1][i][0]*c;
if(qpow[j][i][0]>=phi[i])over[j][i][0]=true,qpow[j][i][0]%=phi[i];
over[j][i][0]|=over[j-1][i][0];
}
}
Rep(i,0,dep){
qpow[0][i][1]=1;
Rep(j,1,bl){
qpow[j][i][1]=qpow[j-1][i][1]*qpow[bl][i][0];
if(qpow[j][i][1]>=phi[i])over[j][i][1]=true,qpow[j][i][1]%=phi[i];
over[j][i][1]|=over[j-1][i][1];
}
}
}
int Qpow(int ind,int p){
flag|=over[ind%bl][p][0]|over[ind/bl][p][1];
int res=qpow[ind%bl][p][0]*qpow[ind/bl][p][1];
if(res>=phi[p])flag=true,res%=phi[p];
return res;
}
int calc(int id,int lim,int d){
flag=false;
if(d==lim){
if(a[id]>=phi[d]){
flag=true;
return a[id]%phi[d];
}
return a[id];
}
int x=calc(id,lim,d+1);
if(flag)x+=phi[d+1],flag=false;
return Qpow(x,d);
}
void pushup(int u){
seg[u].sum=(seg[lc].sum+seg[rc].sum)%p;
seg[u].tim=min(seg[lc].tim,seg[rc].tim);
}
void build(int u,int l,int r){
seg[u].l=l,seg[u].r=r;
if(l==r){
seg[u].sum=a[l];
seg[u].tim=0;
return;
}
int mid=l+r>>1;
build(lc,l,mid);
build(rc,mid+1,r);
pushup(u);
}
void update(int u,int l,int r){
if(seg[u].tim>=dep)return;
if(seg[u].l==seg[u].r){
seg[u].tim++;
seg[u].sum=calc(seg[u].l,seg[u].tim,0);
return;
}
int mid=seg[u].l+seg[u].r>>1;
if(l<=mid)update(lc,l,r);
if(r>mid)update(rc,l,r);
pushup(u);
}
int query(int u,int l,int r){
if(seg[u].l>=l&&seg[u].r<=r)return seg[u].sum;
int mid=seg[u].l+seg[u].r>>1;
int res=0;
if(l<=mid)res+=query(lc,l,r);
if(r>mid)res+=query(rc,l,r);
res%=p;
return res;
}
signed main()
{
read(n),read(m),read(p),read(c);
Rep(i,1,n)read(a[i]);
init();
build(1,1,n);
Rep(i,1,m){
int opt,x,y;
read(opt),read(x),read(y);
if(!opt)update(1,x,y);
else printf("%lld\n",query(1,x,y));
}
return 0;
}