【LuoguP3747】[六省聯考2017] 相逢是問候

題目鏈接

題意

給定一個長度爲 n 的序列 a , 給定一個正整數 c
每次修改操作是把一段區間內的數 xix_i 修改爲 cxic^{x_i}
詢問區間和模 p 的結果

Sol

修改是把一個數變成 cxc^x , 我們很容易想到降冪公式 , 這裏由於 p 不一定與 c 互質 , 那麼我們就使用擴展歐拉公式 , 也就是:
ax={ax    ,    x<φ(p)axmod  φ(p)+φ(p)    ,xφ(p)a^{x}=\begin{cases} a^{x} \;\;,\;\;x<\varphi(p) \\ a^{x\mod\varphi(p)+\varphi(p)}\;\;,x\geq \varphi(p)\\ \end{cases}

φ(p)\varphi(p) 函數對於一個數來說 , 做多取 O(log2 p)O(log_2\ p) 次就會變成 1 , 這樣我們直接暴力在線段樹上修改每一個數 , 直到這個數變成了 cccccc^{c^{c^{c^{c^{\dots}}}}} , 最後的答案我們可以通過直接使用擴展歐拉定理遞歸算出 , 這樣每一個數也最多被修改 loglog 次就不會再變了 , 那麼我們總共的單點修改的次數就會是 O(nlogn)O(nlogn) 級別的。
於是我們只需要考慮單點修改。

對於一次單點修改,我們相當於是把原來的數底下塞上一個 c , 由於這一步操作對於每一層模數都變化了, 並不能直接得到, 那麼必須每次都要單獨算,每次暴力用公式遞歸進去計算的話複雜度變成了 O(nlog3n)O(nlog^3n) 級別的,不能通過所有數據。

考慮優化,我們把快速冪的複雜度優化掉就行了,由於指數的級別最大和 p 差不多,我們對於每一個遞歸中會遇到模數 pp 處理一個 cxc^xc10000xc^{10000x} 就可以 O(1)O(1) 把冪的結果算出來了。

一個比較令人頭痛的問題就是判斷當前的指數是否大於了當前模數的 歐拉函數值,因爲這時我們還要在把指數加上一個 φ(p)\varphi(p) ,只需要每次計算的時候先用 longlong longlong 存好看是否大於模數,用變量記下是否比模數大了就行了。

code:

#include<bits/stdc++.h>
using namespace std;
template<class T>inline void init(T&x){
	x=0;char ch=getchar();bool t=0;
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') t=1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
	if(t) x=-x;return;
}
typedef long long ll;
int n,m,p,c;
const int N=5e4+10;
int a[N];
const int MAXN=N<<2;
int hsize[MAXN],dat[MAXN],times[MAXN],sum[MAXN];
#define ls (u<<1)
#define rs (u<<1|1)
int final_ans;
int phi[N],cnt=0;
const int MAXM=2e4;
struct Powerset{
	int Mod;bool bom[MAXM];int val[MAXM];
	inline void Prepare(int base,int Mo,bool cbom){
		Mod=Mo;bom[0]=0;val[0]=1;bom[1]=cbom,val[1]=base;
		for(int i=2;i<MAXM;++i) {
			ll res=(ll)val[i-1]*base;
			bom[i]=bom[i-1];if(res>=Mod) bom[i]=1;
			val[i]=res%Mod;
		}return;
	}
}Po[61][2];
inline int Sum(int x,int y){x+=y;if(x>=p) x-=p;return x;}
inline int Dif(int x,int y){x-=y;if(x< 0) x+=p;return x;}
inline int fpow(int x,int k,int p){int ret=1;for(;k;k>>=1,x=(ll)x*x%p)if(k&1) ret=(ll)ret*x%p;return ret;}
const int INF=2147483647;
void Build(int u,int l,int r){
	if(l==r) {dat[u]=a[l],hsize[u]=1,sum[u]=a[l],times[u]=0;return;}
	int mid=l+r>>1;
	Build(ls,l,mid),Build(rs,mid+1,r);
	hsize[u]=r-l+1;sum[u]=Sum(sum[ls],sum[rs]);
}
inline int Get_phi(int x){int ret=x;
	for(int i=2;i*i<=x;++i) {if(x%i==0) {ret-=ret/i;x/=i;while(x%i==0)x/=i;}}
	if(x>1) ret-=ret/x;return ret;
}
inline int Calc(int p){
	if(p==1) return 0;int nowphi=Get_phi(p);
	int ret=fpow(c,Calc(nowphi)+nowphi,p)%p;
	phi[++cnt]=nowphi;return ret;
}
inline int Fpow(int x,int k,int P){
	bool boom=0,boompow=0;int ret=1;
	while(k){
		if(k&1) {ll res=(ll)ret*x;if(res>=P) boom=1;boom|=boompow;ret=res%P;}
		boompow=0;ll res=(ll)x*x;
		if(res>=P) boompow=1;x=res%P;k>>=1;
	}if(boom) ret+=P;return ret;
}
inline int Solve(int t,int a,int id){
	if(!t) return a>phi[id]? (a%phi[id]+phi[id]):a;
	int Dat=Solve(t-1,a,id+1);
	if(!id) return (ll)Po[id][0].val[Dat%MAXM]*Po[id][1].val[Dat/MAXM]%phi[id];
	else {
		int r1=Dat%MAXM,r2=Dat/MAXM;
		bool bm=Po[id][0].bom[r1]|Po[id][1].bom[r2];
		if(bm) return (ll)Po[id][0].val[r1]*Po[id][1].val[r2]%phi[id]+phi[id];
		else return (ll)Po[id][0].val[r1]*Po[id][1].val[r2]%phi[id];
	}
}
inline void Modify(int u,int l,int r,int L,int R){
	if(!hsize[u]) return;
	if(l>=L&&r<=R) {
		if(l==r) {++times[u];
			if(times[u]>cnt) sum[u]=final_ans,hsize[u]=0;
			else sum[u]=Solve(times[u],dat[u],0)%p;
			return;
		}int mid=l+r>>1;
		Modify(ls,l,mid,L,R);Modify(rs,mid+1,r,L,R);
		sum[u]=Sum(sum[ls],sum[rs]);hsize[u]=hsize[ls]+hsize[rs];
		return;
	}int mid=l+r>>1;
	if(mid>=L) Modify(ls,l,mid,L,R);
	if(mid< R) Modify(rs,mid+1,r,L,R);
	sum[u]=Sum(sum[ls],sum[rs]);
	hsize[u]=hsize[ls]+hsize[rs];
}
inline int Query(int u,int l,int r,int L,int R){
	if(l>=L&&r<=R) return sum[u];
	int mid=l+r>>1;
	if(mid>=R) return Query(ls,l,mid,L,R);if(mid<L) return Query(rs,mid+1,r,L,R);
	return Sum(Query(ls,l,mid,L,mid),Query(rs,mid+1,r,mid+1,R));
}
namespace Fun{
	bool tag[MAXN];
	inline void set1(int u,int l,int r){sum[u]=r-l+1;tag[u]=1;return;}
	inline void push_down(int u,int l,int mid,int r){if(!tag[u]) return;set1(ls,l,mid),set1(rs,mid+1,r),tag[u]=0;}
	inline void Modify(int u,int l,int r,int L,int R){
		if(l>=L&&r<=R) return set1(u,l,r);
		int mid=l+r>>1;push_down(u,l,mid,r);
		if(mid>=L) Modify(ls,l,mid,L,R);if(mid<R) Modify(rs,mid+1,r,L,R);
		sum[u]=Sum(sum[ls],sum[rs]);
	}
	inline int Query(int u,int l,int r,int L,int R){
		if(l>=L&&r<=R) return sum[u];
		int mid=l+r>>1;push_down(u,l,mid,r);
		if(mid>=R) return Query(ls,l,mid,L,R);if(mid<L) return Query(rs,mid+1,r,L,R);
		return Sum(Query(ls,l,mid,L,mid),Query(rs,mid+1,r,mid+1,R));
	}
	void work(){
		for(int i=1;i<=m;++i) {
			int op,l,r;init(op),init(l),init(r);
			if(op==0) Modify(1,1,n,l,r);
			else printf("%d\n",Query(1,1,n,l,r)%p);
		}
	}
}
inline void Prework(){
	for(int i=0;i<cnt;++i) {
		Po[i][0].Prepare(c,phi[i],c>=phi[i]);
		Po[i][1].Prepare(fpow(c,2e4,phi[i]),phi[i],1);
	}return;
}
int main()
{
	init(n),init(m),init(p),init(c);
	for(int i=1;i<=n;++i) init(a[i]);
	Build(1,1,n);
	if(c==1) Fun::work();
	else {
		phi[0]=p;final_ans=Calc(p);
		reverse(phi+1,phi+1+cnt);
		Prework();
		for(int i=1;i<=m;++i) {
			int op,l,r;init(op),init(l),init(r);
			if(op==0) Modify(1,1,n,l,r);
			else printf("%d\n",Query(1,1,n,l,r)%p);
		}
	}
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章