题意:给定一个长度为 nnn 的序列 aia_iai,维护 mmm 次操作:
- 区间执行 ai←caia_i \leftarrow c^{a_i}ai←cai;
- 区间求和 模 ppp。
其中 p,cp,cp,c 对所有操作相同。
n,m≤5×104,p≤108n,m\leq 5\times 10^4,p\leq 10^8n,m≤5×104,p≤108
不建议写,真的浪费生命
根据扩展欧拉定理,显然修改 O(logp)O(\log p)O(logp) 次就没用了,所有数总共只会修改 O(nlogP)O(n\log P)O(nlogP) 次。
但是暴力的话循环是占复杂度的,我们需要快速判断某个区间是否还需要修改。
似乎就只能线段树了?
暴力预处理出每个数 φ 60\varphi \text{ } 60φ 60 次内的结果,然后线段树维护区间修改次数最小值和当前的区间和。修改的时候如果当前区间最小修改次数达到 606060 就退出,否则暴力递归。
复杂度 O(nlog2P)O(n\log^2 P)O(nlog2P) ,会被卡,根号预处理快速幂可以 O(nlogP)O(n\log P)O(nlogP) 。
听上去还挺美好,然而这题最恶心的地方在判断扩展欧拉定理的适用条件……
方法是在快速幂(或预处理幂)的时候记录是否取过模。
Q:如何证明不会出现 x>φ(p),cx%φ(p)+φ(p)<px>\varphi (p),c^{x \%\varphi(p)+\varphi(p)}<px>φ(p),cx%φ(p)+φ(p)<p ,然后继续迭代的时候崩掉的情况?
A:实际上 cφ(p)≥pc^{\varphi(p)} \geq pcφ(p)≥p 一般都是对的。
考虑计算 φ(p)\varphi(p)φ(p) 的一个很松的下界:φ(p)\varphi(p)φ(p) 考虑了每个质因子 xxx ,然后 p←p×x−1xp\leftarrow p\times \dfrac {x-1}xp←p×xx−1
根据糖水原理,这个 xxx 越小效果会越明显。而 xxx 小的时候能得到的质因子也会更多。
而 10810^8108 以内的数最多只有 888 个质因子,所以可以估算一个下界 φ(p)p≥12×23×45×⋯×1819≈0.17\dfrac{\varphi(p)}{p}\geq\dfrac 12 \times \dfrac 23\times \dfrac 45\times \dots\times \dfrac {18}{19} \approx 0.17pφ(p)≥21×32×54×⋯×1918≈0.17
相当于要证明: 2p/7≥p2^{p/7}\geq p2p/7≥p ,前者增长远快于后者,ppp 足够大(也就几十)的时候该式成立。
小数据暴力打表知只有个 p=6p=6p=6 ,但 666 只能取 222 次 φ\varphiφ ,所以也崩不了
证毕
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <vector>
#include <bitset>
#include <utility>
#define MAXN 50005
#define re register
using namespace std;
typedef long long ll;
int n,q,c,flag;
inline int mul(const int& a,const int& b,const int& MOD)
{
ll c=(ll)a*b;
if (c>=MOD) flag=1;
return c%MOD;
}
int m[60],len;
typedef pair<int,int> pi;
const int N=2e4;
pi p1[60][N+5],p2[60][N+5];//i,i*10000
inline void init()
{
for (int T=0;T<=len;T++)
{
p1[T][0]=p2[T][0]=make_pair(1,0);
flag=0;
for (int i=1;i<N;i++) p1[T][i].first=mul(p1[T][i-1].first,c,m[T]),p1[T][i].second=flag;
p2[T][1].first=mul(p1[T][N-1].first,c,m[T]),p2[T][1].second=flag;
for (int i=2;i<=N;i++) p2[T][i].first=mul(p2[T][i-1].first,p2[T][1].first,m[T]),p2[T][i].second=flag;
}
}
inline int qpow(int p,int i)
{
pi x=p1[i][p%N],y=p2[i][p/N];
flag|=(x.second|y.second);
return mul(x.first,y.first,m[i]);
}
inline int calcphi(int x)
{
int ans=1;
for (int i=2;i*i<=x;i++)
if (x%i==0)
{
ans*=i-1,x/=i;
while (x%i==0) ans*=i,x/=i;
}
if (x>1) ans*=x-1;
return ans;
}
int a[MAXN][60];
#define lc p<<1
#define rc p<<1|1
int sum[MAXN<<2],mcnt[MAXN<<2];
inline void update(int p){mcnt[p]=min(mcnt[lc],mcnt[rc]),sum[p]=(sum[lc]+sum[rc])%m[0];}
void build(int p,int l,int r)
{
if (l==r)
{
scanf("%d",&a[l][0]);
for (int i=1;i<=len;i++)
{
flag=0;
int x=a[l][0];
for (int j=i;j>=1;j--) x=qpow(x+m[j]*flag,j-1);
a[l][i]=x;
}
sum[p]=a[l][0];
return;
}
int mid=(l+r)>>1;
build(lc,l,mid);build(rc,mid+1,r);
update(p);
}
void modify(int p,int l,int r,int ql,int qr)
{
if (qr<l||r<ql) return;
if (mcnt[p]>=len) return;
if (l==r) return (void)(sum[p]=a[l][++mcnt[p]]);
int mid=(l+r)>>1;
modify(lc,l,mid,ql,qr),modify(rc,mid+1,r,ql,qr);
update(p);
}
int query(int p,int l,int r,int ql,int qr)
{
if (qr<l||r<ql) return 0;
if (ql<=l&&r<=qr) return sum[p];
int mid=(l+r)>>1;
return (query(lc,l,mid,ql,qr)+query(rc,mid+1,r,ql,qr))%m[0];
}
int main()
{
scanf("%d%d%d%d",&n,&q,&m[0],&c);
for (len=1;(m[len]=calcphi(m[len-1]))>1;++len);
m[++len]=1;
init();
build(1,1,n);
while (q--)
{
int t,l,r;
scanf("%d%d%d",&t,&l,&r);
if (t==0) modify(1,1,n,l,r);
else printf("%d\n",query(1,1,n,l,r));
}
return 0;
}
这篇博客讨论了一个关于区间操作的问题,其中涉及区间元素的幂次修改和区间和的求解。作者指出,由于扩展欧拉定理,对于每次修改操作的时间复杂度优化至关重要。通过预处理和线段树的数据结构,可以将复杂度降低到O(nlogP)。博客中详细解释了如何避免在使用扩展欧拉定理时可能出现的错误,并通过数学分析证明了其正确性。最后,给出了问题的解决方案和代码实现。
1万+

被折叠的 条评论
为什么被折叠?



