1. 程式人生 > >2018.10.02【校內模擬】序列維護(分塊)(廣義尤拉定理)

2018.10.02【校內模擬】序列維護(分塊)(廣義尤拉定理)

【描述】

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

【輸入格式】

第一行兩個整數 n,m ,表示序列長度和運算元。 接下來一行, n 個整數 a_i 表示這個序列。 接下來 m 行,可能是以下兩種操作之一: 1 l,r,x,表示區間[l,r] 加上 x。 2 l,r,p ,表示進行一次查詢,模數為 p。

【輸出格式】

對於每個操作 2,輸出一行表示答案。

【輸入樣例 1】

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】

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

解析:

提高組模擬考廣義尤拉定理真的扯。。。 我樣例都過了開始寫對拍的時候居然聽到有人問老師樣例是不是錯了。。。 錯你個 (和諧) 啊。。自己不會廣義尤拉定理就敢明目張膽的把指數拿來對PP取模嗎?就不能老老實實的一步一步算嗎?

思路:

這道題顯然是個廣義尤拉定理,而且沒有費馬小定理部分分。emmmm。妙不可言。。。 考慮有讀者不瞭解廣義尤拉定理,博主在此敘述一下。 對於整數a,ma,m,若a,ma,m互質,則ananmodϕ(m)(modm)a^n\equiv a^{n\text{ }mod\text{ }\phi (m)}(mod\text{ }m)。 若a,ma,m不互質,則當nϕ(m)n\ge \phi(m)時,有ananmodϕ(m)+ϕ(m)(modm)a^n\equiv a^{n\text{ }mod\text{ }\phi(m)+\phi(m)}(mod\text{ }m)

而一個整數mm最多取lgnlg\text{ }nϕ\phi就會變成1。而任意數modmod 1結果都是0,這時候應該返回1,因為廣義尤拉定理意義下不允許指數為0,必須為ϕ(n)\phi(n)

對於加法我採用的是分塊維護,這樣可以做到任何時候的O(1)O(1)詢問某個位置的當前值,詳細實現看程式碼。

而主要考慮詢問。底數為0直接返回,不能使用廣義尤拉定理。 因為22222>2e72^{2^{2^{2^{2}}}}>2e7(五個2),且1n=11^n=1,所以我們只考慮後面5位中第一個為1的位置前或全部位置,如果指數炸出了當前模數的範圍就需要遞迴下去重新做廣義尤拉定理。如果沒有,就直接快速冪搞一波就行了。

程式碼:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

inline
ll getint(){
	re ll num;
	re char c;
	re bool f=0;
	while(!isdigit(c=gc()));num=c^48;
	while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
	return num;
}

inline
void outint(ll a){
	static char ch[23];
	assert(a>=0);
	if(a==0)pc('0');
	if(a<0)pc('-'),a=-a;
	while(a)ch[++ch[0]]=a-a/10*10,a/=10;
	while(ch[0])pc(ch[ch[0]--]^48);
}

cs int P=20000007;
int phi[P];
int prime[P],pcnt;
bitset<P> mark;
inline
void linear_sieves(int len=P-7){
	mark[1]=true;
	phi[1]=1;
	for(int re i=2;i<=len;++i){
		if(!mark[i])prime[++pcnt]=i,phi[i]=i-1;
		for(int re j=1;j<=pcnt&&i*prime[j]<=len;++j){
			mark[i*prime[j]]=true;
			if(i==i/prime[j]*prime[j]){
				phi[i*prime[j]]=phi[i]*prime[j];
				break;
			}
			phi[i*prime[j]]=phi[i]*(prime[j]-1);
		}
	}
}

inline
ll quickpow(ll a,ll b,ll mod){
	ll ans=1;
	while(b){
		if(b&1)ans=ans*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return ans;
}

cs int N=5000090;
int B,bcnt=1;
int block[N];
ll add[N];
ll a[N];
int n,m;

inline
ll query(int i){return a[i]+add[block[i]];}

inline
ll solve(int l,int r,int yql){   
	if(query(l)%yql==0)return 0;   
	if(yql==1)return 1;   
	if(l==r)return query(l)%yql+(query(l)>=yql)*yql;   
	int f=min(r,l+5);   
	for(int i=l+1;i<=f;i++)if(query(i)==1){f=i;break;}   
	ll last=query(f),q=0;   
	for(int i=f-1;i>=l+1;i--){   
		q=last,last=1;   
		while(q--){
			last*=query(i);   
			if(last>=phi[yql])return quickpow(query(l)%yql,solve(l+1,r,phi[yql])+phi[yql],yql);
		}   
	}   
	return quickpow(query(l)%yql,last,yql);   
}   
signed main(){
	linear_sieves();
	n=getint(),m=getint();
	B=sqrt(n);
	for(int re i=1;i<=n;++i){
		a[i]=getint();
		block[i]=bcnt;
		if(i%B==0)++bcnt;
	}
	block[n+1]=bcnt+1;
	while(m--){
		int op=getint();
		int l=getint(),r=getint();
		ll x=getint();
		switch(op){
			case 1:{
				if(block[l]==block[r]){
					for(int re i=l;i<=r;++i)a[i]+=x;
					break;
				}
				for(int re i=l;block[i]==block[l];++i)a[i]+=x;
				for(int re i=r;block[i]==block[r];--i)a[i]+=x;
				for(int re i=block[l]+1;i<block[r];++i)add[i]+=x;
				break;
			}
			case 2:{
				int res=solve(l,r,x)%x;
				outint(res);pc('\n');
				break;
			}
		}
	}
	return 0;
}