1. 程式人生 > >2018.10.02 NOIP模擬 序列維護(線段樹+廣義尤拉定理)

2018.10.02 NOIP模擬 序列維護(線段樹+廣義尤拉定理)

描述

給出一個長度為n的序列,每個位置有個數字Ai,有2個操作: 1、區間修改,將[L,R]區間的數字加上一個數 2、區間查詢[l,r] 查詢:alal+1al+2....armodpa_l^{a_{l+1}^{a_{l+2}^{\text{....}^{a_r}}}} \ mod \ p

輸入

第一行兩個整數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%的資料,n,m500000n,m≤500000, 序列中每個數在 [1,2109][1,2*10^9 ]內,p2107p≤2*10^7 , 每次加上的數在[0,2109][0,2*10^9]

一道比較好的線段樹。 考試時線性篩打錯了於是棄療。 60分暴力中有20分的快速冪乘爆了於是最後40分滾粗。 正解並不難想。 每次區間加打懶標記就行了。 區間查詢要用到廣義尤拉定理。 我們會發現每次遞迴都會將mod變成ϕ

(mod)\phi(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;
}