【洛谷P3934】炸脖龙 I

题目

题目链接:https://www.luogu.com.cn/problem/P3934
给一个长为 (n) 的序列,(m) 次操作,每次操作:

  1. 区间 ([l,r])(x)
  2. 对于区间 ([l,r]),查询:(a[l]^{a[l+1]^{a[l+2]^{dots ^{a[r]}}}} mod p)

(n,mleq 5 imes 10^5)(pleq 2 imes 10^7)

思路

扩展欧拉定理:

[a^bequiv left{egin{align*}a^bspace (b<varphi(p))\ a^{b ext{ mod }varphi(p) +varphi(p)}space(bgeqvarphi(p)) end{align*} ight.pmod p ]

(g(l,r)=a[l]^{a[l+1]^{a[l+2]^{dots ^{a[r]}}}}spacespacespace)(f(l,r,p)=g(l,r)mod p)
那么有

[f(l,r,p)=a[l]^{f(l+1,r,varphi(p))+[g(l+1,r)geq varphi(p)] imes varphi(p)}mod p ]

而因为 (varphi(varphi(p)))这样一直递归下去,(O(log p)) 次后就会变成 (1),而当模数 (p=1) 时,显然答案为 (0)。所以说我们计算 (f) 只会递归 (O(log p)) 层。
那么显然只需要支持区间加单点查询,树状数组即可。
为了降低代码复杂度,可以建一个类,第一维表示值,第二维表示是否超过当前的模数。然后重载一下 * 运算符。
注意指数要讨论是否需要 (+varphi(p)) 这些细节。
时间复杂度 (O(mlog plog n))

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=500010,M=20000010;
int n,m,Q,prm[M],phi[M];
bool v[M];

int read()
{
	int d=0; char ch=getchar();
	while (!isdigit(ch)) ch=getchar();
	while (isdigit(ch)) d=(d<<3)+(d<<1)+ch-48,ch=getchar();
	return d;
}

void findprm(int n)
{
	for (int i=2;i<=n;i++)
	{
		if (!v[i]) prm[++m]=i,phi[i]=i-1;
		for (int j=1;j<=m;j++)
		{
			if (i>n/prm[j]) break;
			v[i*prm[j]]=1; phi[i*prm[j]]=phi[i]*(prm[j]-1);
			if (!(i%prm[j])) { phi[i*prm[j]]=phi[i]*prm[j]; break; }
		}
	}
}

struct node
{
	ll a,b;
	
	friend node mul(node x,node y,ll p)
	{
		return (node){x.a*y.a%p,max((ll)(x.a*y.a>=p),max(x.b,y.b))};
	}
};

node fpow(node x,ll k,ll mod)
{
	node ans=(node){1,0};
	if (x.a>=mod) x.a%=mod,ans.b=1;
	for (;k;k>>=1,x=mul(x,x,mod))
		if (k&1) ans=mul(ans,x,mod);
	return ans;
}

struct BIT
{
	ll c[N];
	
	void add(int x,int v)
	{
		for (int i=x;i<=n;i+=i&-i)
			c[i]+=v;
	}
	
	ll query(int x)
	{
		ll ans=0;
		for (int i=x;i;i-=i&-i)
			ans+=c[i];
		return ans;
	}
}bit;

node query(int l,int r,ll p)
{
	ll x=bit.query(l);
	if (p==1) return (node){0,1};
	if (l==r) return (node){x%p,(ll)(x>=p)};
	node res=query(l+1,r,phi[p]);
	return fpow((node){x,0},res.a+res.b*phi[p],p);
}

int main()
{
	findprm(M-1);
	n=read(); Q=read();
	for (int i=1;i<=n;i++) 
	{
		int x=read();
		bit.add(i,x); bit.add(i+1,-x);
	}
	while (Q--)
	{
		int opt=read(),l=read(),r=read(),p=read();
		if (opt==1)
			bit.add(l,p),bit.add(r+1,-p);
		else
			cout<<query(l,r,p).a<<"
";
	}
	return 0;
}
原文地址:https://www.cnblogs.com/stoorz/p/15260243.html