把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷3934】[Ynoi2016] 炸脖龙 I(奈芙莲树)

题目链接

  • 给定一个长度为 \(n\) 的序列。\(q\) 次操作,分为两种:给区间 \([l,r]\) 加上一个数 \(v\);对区间 \([l,r]\) 询问 \(a_l^{a_{l+1}^{\ \cdots\ ^{a_r}}}\operatorname{mod}p\)
  • \(1\le n,q\le5\times10^5\)\(1\le p\le2\times10^7\)\(1\le a_i\le2\times10^9\)\(0\le v\le2\times10^9\)

这题的做法因为与此题完全一样的 【洛谷4691】Nephren Ruq Insania 一题而被称作 奈芙莲树,然而似乎奈芙莲树也只能做这一题。。。

树状数组维护区间加

对于区间加操作,我们用树状数组维护即可。

因为在接下来的询问过程中我们只会涉及到单点求值操作。

扩展欧拉定理

对于任意模数 \(p\),都有:

\[a^x\equiv \begin{cases} a^x&x < \phi(p),\\ a^{x\operatorname{mod}\phi(p)+\phi(p)}&x\ge\phi(p) \end{cases} (\operatorname{mod}p) \]

由于 \(p\) 在取至多 \(\log\)\(\phi(p)\) 后就会变成 \(1\),而任意数模 \(1\) 都为 \(0\),我们可以直接暴力计算。

但扩展欧拉定理最麻烦的一点就是讨论指数是否大于等于 \(\phi(p)\)

首先,如果区间中存在某个位置 \(t\) 满足 \(a_t=1\),因为 \(1\) 的任意次方都为 \(1\),所以我们可以直接把 \(r\) 设为 \(t\)。那么现在 \([l,r)\) 中的数都非 \(1\) 了。

又由于 \(2^{2^{2^{2^2}}}\) 已经大于 \(p\) 的上界了,所以这样一来只要运算五位就肯定会超出 \(\phi(p)\),而既然至多只有 \(5\) 位,我们完全可以暴力运算一下,减少讨论。

如果暴力算出的结果超出 \(\phi(p)\),就递归求解区间 \([l,r]\)\(\phi(p)\) 的答案加上 \(\phi(p)\) 作为指数;如果不超出 \(\phi(p)\) 说明剩余区间不到 \(5\) 位,且当前求出的就是剩余区间的值,直接作为指数即可。

代码:\(O(n\log^2 p)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Rg register
#define RI Rg int
#define Cn const
#define CI Cn int&
#define I inline
#define W while
#define N 500000
#define SZ 20000000
#define V(i) (a[i]+T.Q(i))
#define LL long long
using namespace std;
int n,a[N+5];set<int> S;set<int>::iterator it,jt;
I int QP(RI x,LL y,CI p) {RI t=1;W(y) y&1&&(t=1LL*t*x%p),x=1LL*x*x%p,y>>=1;return t;}
namespace FastIO
{
	#define FS 100000
	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
	#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
	int OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
	I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
	Tp I void writeln(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc('\n');}
}using namespace FastIO;
int Pt,P[SZ+5],phi[SZ+5];I void Sieve()//线性筛预处理欧拉函数
{
	phi[1]=1;for(RI i=2,j;i<=SZ;++i) for(!P[i]&&(phi[P[++Pt]=i]=i-1),j=1;i*P[j]<=SZ;++j)
		if(P[i*P[j]]=1,i%P[j]) phi[i*P[j]]=phi[i]*(P[j]-1);else {phi[i*P[j]]=phi[i]*P[j];break;}
}
struct TreeArray//树状数组维护区间加
{
	LL a[N+5];I void U(RI x,CI v) {W(x<=n) a[x]+=v,x+=x&-x;}I LL Q(RI x) {LL t=0;W(x) t+=a[x],x-=x&-x;return t;}
	I void U(CI l,CI r,CI v) {U(l,v),U(r+1,-v);}
}T;
I int Q(CI l,CI r,CI p)//询问[l,r]模p的答案
{
	RI v=V(l)%p;if(!v) return 0;if(l==r) return v;RI m=min(l+5,r),t=phi[p];LL o,x=V(m),y;
	for(RI i=m-1;i^l;--i) {y=x,x=1,o=V(i);W(y--) if(1.0L*x*o>t) return QP(v,Q(l+1,r,t)+t,p);else x*=o;}return QP(v,x,p);//暴力算5位,如果过程中超出φ(p)就递归询问,否则直接作为模数
}
int main()
{
	RI Qt,i,op,x,y,z,t;for(read(n,Qt),Sieve(),i=1;i<=n;++i) read(a[i]),a[i]==1&&(S.insert(i),0);W(Qt--)
	{
		if(read(op,x,y,z),op==1) {z&&(S.erase(S.lower_bound(x),S.upper_bound(y)),T.U(x,y,z),0);continue;}
		S.lower_bound(x)!=S.end()&&(y=min(y,*S.lower_bound(x))),writeln(Q(x,y,z)%z);//将右端点与区间中第一个1的位置取min
	}return clear(),0;
}
posted @ 2021-11-12 07:28  TheLostWeak  阅读(191)  评论(0编辑  收藏  举报