2018.10.02 NOIP模擬 序列維護(線段樹+廣義尤拉定理)
阿新 • • 發佈:2018-12-13
描述
給出一個長度為n的序列,每個位置有個數字Ai,有2個操作: 1、區間修改,將[L,R]區間的數字加上一個數 2、區間查詢[l,r] 查詢:
輸入
第一行兩個整數n,m ,表示序列長度和運算元。 接下來一行, n個整數a_i 表示這個序列。 接下來m 行,可能是以下兩種操作之一: 1 l,r,x,表示區間[l,r] 加上x 2 l,r,p ,表示進行一次查詢,模數為p
輸出
對於每個操作2,輸出一行表示答案
樣例輸入
6 4 1 2 3 4 5 6 2 1 2 10000007 2 2 3 5 1 1 4 1 2 2 4 10
樣例輸出
1 3 1
【輸入樣例2】
5 5 2 3 3 3 3 1 1 1 530739835 2 1 1 8356089 2 1 4 5496738 1 1 2 66050181 1 2 4 138625417
【輸出樣例2】
4306230 697527
提示
測試點 | n的範圍 | m的範圍 | 特殊限制 |
---|---|---|---|
1 | n = 5 | m = 5 | a_i ≤3 |
2 | n = 1000 | m = 1000 | 查詢區間長度為1 |
3 | n = 100000 | m = 100000 | 查詢區間長度為1 |
4 | n = 1000 | m = 1000 | 查詢區間長度不大於2 |
5 | n = 100000 | m = 100000 | 查詢區間長度不大於2 |
6 | n = 1000 | m = 1000 | a_i ≤2 |
7 | n = 1000 | m = 1000 | p = 2 |
8 | n = 100000 | m = 100000 | p = 2 |
9 | n = 1000 | m = 1000 | p ≤100000 |
10 | n = 500000 | m = 500000 | 無 |
對於100%的資料,, 序列中每個數在 內, , 每次加上的數在內
一道比較好的線段樹。 考試時線性篩打錯了於是棄療。 60分暴力中有20分的快速冪乘爆了於是最後40分滾粗。 正解並不難想。 每次區間加打懶標記就行了。 區間查詢要用到廣義尤拉定理。 我們會發現每次遞迴都會將mod變成,這樣遞迴log次就完了。 程式碼:
#include<bits/stdc++.h>
#define ll long long
#define lc (p<<1)
#define rc (p<<1|1)
#define mid (T[p].l+T[p].r>>1)
#define N 500005
using namespace std;
inline ll read(){
ll ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
int pri[1300005],tot=0,n,m,tim[N];
ll phi[20000005],a[N];
bool vis[20000005];
inline void init(int len=20000000){
phi[1]=1;
for(int i=2;i<=len;++i){
if(!vis[i])pri[++tot]=i,phi[i]=i-1;
for(int j=1;j<=tot;++j){
int k=pri[j]*i;
if(k>len)break;
vis[k]=1;
if(i%pri[j]==0){phi[k]=phi[i]*pri[j];break;}
phi[k]=phi[i]*(pri[j]-1);
}
}
}
struct Node{int l,r;ll val,lz;}T[N<<2];
inline void build(int p,int l,int r){
T[p].l=l,T[p].r=r;
if(l==r){T[p].val=a[l];return;}
build(lc,l,mid),build(rc,mid+1,r);
}
inline void pushnow(int p,ll v){T[p].lz+=v,T[p].val+=v;}
inline void pushdown(int p){if(T[p].lz)pushnow(lc,T[p].lz),pushnow(rc,T[p].lz),T[p].lz=0;}
inline void update(int p,int ql,int qr,ll v){
if(ql>T[p].r||qr<T[p].l)return;
if(ql<=T[p].l&&T[p].r<=qr)return pushnow(p,v);
pushdown(p);
if(qr<=mid)update(lc,ql,qr,v);
else if(ql>mid)update(rc,ql,qr,v);
else update(lc,ql,mid,v),update(rc,mid+1,qr,v);
}
inline ll query(int p,int k){
if(T[p].l==T[p].r)return T[p].val;
pushdown(p);
if(k<=mid)return query(lc,k);
return query(rc,k);
}
inline ll query(int pos){
if(tim[pos]==m)return a[pos];
tim[pos]=m;
return a[pos]=query(1,pos);
}
inline ll ksm(ll x,ll p,ll mod){
ll ret=1;
while(p){
if(p&1)ret=ret*x%mod;
x=x*x%mod,p>>=1;
}
return ret;
}
inline ll ask(int ql,int qr,ll mod){
ll tmpl=query(ql);
if(tmpl%mod==0)return 0;
if(mod==1)return 1;
if(ql==qr)return tmpl%mod+(tmpl>=mod)*mod;
int L=ql,R=min(qr,L+5);
for(int i=L+1;i<=R;++i)if(query(i)==1){R=i;break;}
ll mul=query(R),tmp,ttmp;
for(int i=R-1;i>L;--i){
tmp=mul,mul=1,ttmp=query(i);
while(tmp--){
mul*=ttmp;
if(mul>=phi[mod])return ksm(tmpl%mod,ask(ql+1,qr,phi[mod])%phi[mod]+phi[mod],mod);
}
}
return ksm(tmpl%mod,mul,mod);
}
int main(){
init(),memset(tim,-1,sizeof(tim)),n=read(),m=read();
for(int i=1;i<=n;++i)a[i]=read();
build(1,1,n);
while(m--){
int op=read(),l=read(),r=read();
ll v=read();
if(op==1&&!v)continue;
if(op==1)update(1,l,r,v);
else printf("%lld\n",ask(l,r,v)%v);
}
return 0;
}