Informatik verbindet dich und mich.
信息将你我连结。
B 君希望以维护一个长度为 \(n\) 的数组,这个数组的下标为从 \(1\) 到 \(n\) 的正整数。
一共有 \(m\) 个操作,可以分为两种:
0 l r
表示将第 \(l\) 个到第 \(r\) 个数( \(a_l,a_{l+1} ...a_r\))中的每一个数 \(a_i\) 替换为 \(c^{a_i}\),即 \(c\) 的 \(a_i\) 次方,其中 \(c\) 是输入的一个常数,也就是执行赋值 \(a_i = c^{a_i}\)。
1 l r
求第 \(l\) 个到第 \(r\) 个数的和,也就是输出: \(\sum_{i=l}^{r}a_i\)
因为这个结果可能会很大,所以你只需要输出结果 \(\bmod \space p\) 的值即可。
第一行有四个整数 \(n, m, p, c\),所有整数含义见问题描述。
接下来一行 \(n\) 个整数,表示 \(a\) 数组的初始值。
接下来 \(m\) 行,每行三个整数,其中第一个整数表示了操作的类型。
对于每个询问操作,输出一行,包括一个整数表示答案 \(\bmod \space p\) 的值。
4 4 7 2 1 2 3 4 0 1 4 1 2 4 0 1 4 1 1 3
0 3
1 40 19910626 2 0 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1
1 2 4 16 65536 11418102 18325590 13700558 13700558 13700558 13700558 13700558 13700558 13700558 13700558 13700558 13700558 13700558 13700558 13700558
【数据范围】
对于 \(0\%\) 的测试点,和样例一模一样;
对于另外 \(10\%\) 的测试点,没有修改;
对于另外 \(20\%\) 的测试点,每次修改操作只会修改一个位置(也就是 \(l = r\) ),并且每个位置至多被修改一次;
• 对于另外 \(10\%\) 的测试点, \(p = 2\);
对于另外 \(10\%\) 的测试点, \(p = 3\);
对于另外 \(10\%\) 的测试点, \(p = 4\);
对于另外 \(20\%\) 的测试点, \(1\le n,m \le 100\);
对于 \(100\%\) 的测试点, \(1\le n,m \le 5\times 10^4\),\(1 \le p \le 10^8\),\(0< c < p\),\(0 \le a_i < p\)。
这个操作看上去就十分神仙,所以我们考虑使用 Segment beats 均摊方法。
受到第二个样例的启发,我们猜想可能某个数操作到一定地步后便不会再动了。
我们考虑拓展欧拉定理。
其叙述是若 \(b\ge\varphi(p)\),则有 \(a^b\equiv a^{b\,\bmod\,\varphi(p)+\varphi(p)}\pmod p\)
我们现在想象一个无限高的幂塔,即:
\(c^{c^{c^{c^{...}}}}\bmod p\)
那么第一层的 \(c\) 需要 \(\bmod p\),第二层的 \(c\) 便需要 \(\bmod \varphi(p)\),第三层的 \(c\) 则需要 \(\bmod\varphi(\varphi(p))\) ......
什么时候像样例二一样终止?
我们发现若我们设定一个变量 \(x=p\),不断地令 \(x=\varphi(x)\) 的话,那么 \(x\) 将在 \(O(\log p)\) 次后降为 \(1\)。
如何证明?
由 \(\varphi\) 的计算式 \(\varphi(x)=\prod(p-1)p^{k-1}\) 和 \(\varphi(x)=x\prod\frac{p-1}{p}\) 可以得到两个结论。
\(1.\) 若 \(x\) 为奇数,则 \(\varphi(x)\) 为偶数。
证明的话,由于它是奇数,所以他的所有质因子 \(p\) 都是奇数,故而所有 \((p-1)p^{k-1}\) 都是偶数,它们之积也自然是偶数了。
\(2.\) 若 \(x\) 为偶数,则 \(\varphi(x)\leq\frac{x}{2}\)。
证明的话,由于偶数,所以有一个质因子 \(p\),所以 \(x\) 至少乘了一个 \(\frac{1}{2}\) 的系数。
综上可知,\(x\) 每迭代两次便会下降一半,由此,\(x\) 将在 \(O(\log p)\) 次后降为 \(1\) 的结论得到证明。
然后考虑变成 \(1\) 之后有什么用。
举一个例子。
\(c^{c^{c^{a_i}}}\) 和 \(c^{c^{a_i}}\) 有很大的不同吧 \((a_i,c\ne0)\)。
但是如果到了第三层模数已经变为 \(1\) 了呢?
那么第三层的 \(c^{a_i}\) 和 \(a_i\) 就本质上无区别,因为它们都 \(\ge1\) 且 \(\bmod 1=0\)。
所以说,如果 \(\varphi(\varphi(p))=1\,(\)第三层模数为 \(1)\),我们就会有:
\(c^{c^{c^{a_i}}}\equiv c^{c^{a_i}}\pmod p\)
同理 ,\(c\) 更多的时候都是相等的。
所以我们得到了一个重要的结论,每个位置上的数在进行 \(O(\log p)\) 次操作后继续操作不会改变。
套用 Segment beats 均摊方法即可。
实现起来会稍微有一点麻烦,麻烦的主要来源是拓展欧拉定理的适用条件 \(b\ge\varphi(p)\) ,因此我们每取一次模还需要判定该数与模数的大小关系以决定该数是否要 \(+\varphi(p)\),虽然麻烦但并不难,读者可自行考虑实现。
然后你每一次求 \(c^{c^{c^{a_i}}}(k 个 c)\) 的话使用朴素方法是 \(O(k\log v)\) 的 \((v\) 为值域\()\),因为每层都要做一遍快速幂,由于 \(k_{\operatorname{max}}=O(\log p)\),而每个数又都要操作 \(O(\log p)\) 次才能最终保持不动,所以总复杂度是 \(O(n\log^2 p\log v)\) 难以通过。
考虑省掉快速幂的一个 \(\log\)。
观察到我们每次要求形如 \(c^b\bmod k\) 的式子。
而底数 \(c\) 固定,\(k\) 至多只有 \(O(\log p)\) 个,那么可以使用光速幂对于每一个模数都 \(O(\sqrt v)\) 预处理,每次 \(O(1)\) 查询,总复杂度 \(O(\sqrt v\log p+n\log p\log v)\) ,可以通过本题。
#include<bits/stdc++.h> using namespace std; #define inf 0x7fffffff #define timeused() (double)clock()/CLOCKS_PER_SEC #define rep(i,a,b) for(register int i=a,i##end=b;i<=i##end;++i) #define repp(i,a,b) for(register int i=a,i##end=b;i>=i##end;--i) #define mp make_pair #define pb push_back typedef long long ll; typedef unsigned long long ull; template<typename T> inline T rd(T& x){ T f=1;x=0;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(T)(c-'0'); x*=f; return x; } ll ls(ll x){return x<<1;} ll rs(ll x){return x<<1|1;} ll n,m,c,a[50005],p[50005],tot,mi[50005][55],yz,B[55][10005],G[55][10005],poww[55],tmp[55]; struct node{ ll now,sum; bool ok; void clear(){ok=now=sum=0;} }b[200005]; node merge(node x,node y){ node z; z.ok=(x.ok&&y.ok); z.sum=(x.sum+y.sum)%p[0]; return z; } void maintain(ll x){b[x]=merge(b[ls(x)],b[rs(x)]);} ll phi(ll x){ ll ans=x; rep(i,2,sqrt(x)){ if(x%i==0){ ans/=i; ans*=i-1; } while(x%i==0) x/=i; } if(x>1){ ans/=x; ans*=x-1; } return ans; } void build(ll x,ll l,ll r){ if(l==r){ b[x].clear(); b[x].sum=mi[l][0]; b[x].ok=b[x].now>=tot; return; } ll mid=l+r>>1; build(ls(x),l,mid); build(rs(x),mid+1,r); maintain(x); } void upd(ll x,ll l,ll r,ll L,ll R){ if(b[x].ok) return; if(l==r){ ++b[x].now; b[x].sum=mi[l][b[x].now]; b[x].ok=b[x].now>=tot; return; } ll mid=l+r>>1; if(L<=mid) upd(ls(x),l,mid,L,R); if(R>mid) upd(rs(x),mid+1,r,L,R); maintain(x); } ll query(ll x,ll l,ll r,ll L,ll R){ if(L<=l&&R>=r) return b[x].sum%p[0]; ll ans=0,mid=l+r>>1; if(L<=mid) ans=(ans+query(ls(x),l,mid,L,R))%p[0]; if(R>mid) ans=(ans+query(rs(x),mid+1,r,L,R))%p[0]; return ans%p[0]; } int main(){ rd(n); rd(m); rd(p[0]); yz=10000; rd(c); poww[0]=1; rep(i,1,35){ poww[i]=poww[i-1]*c; if(poww[i]>=inf) poww[i]=inf; } while(p[tot]-1){ ++tot; p[tot]=phi(p[tot-1]); } p[++tot]=1; p[++tot]=1; p[++tot]=1; rep(i,0,tot){ B[i][0]=G[i][0]=1; rep(j,1,yz) B[i][j]=B[i][j-1]*c%p[i]; G[i][1]=B[i][yz]; rep(j,2,yz) G[i][j]=G[i][j-1]*B[i][yz]%p[i]; } rep(i,1,n){ rd(a[i]); tmp[0]=a[i]; rep(j,1,tot){ if(tmp[j-1]>50) tmp[j]=inf; else tmp[j]=poww[tmp[j-1]]; } rep(j,0,tot){ if(c==1){ mi[i][j]=1; if(!j) mi[i][j]=a[i]; continue; } mi[i][j]=a[i]%p[j]; if(a[i]>=p[j]) mi[i][j]+=p[j]; repp(k,j-1,0){ mi[i][j]=B[k][mi[i][j]%yz]*G[k][mi[i][j]/yz]%p[k]; if(tmp[j-k]>=p[k]&&k) mi[i][j]+=p[k]; } } } build(1,1,n); while(m--){ ll opt,l,r; rd(opt); rd(l); rd(r); if(!opt) upd(1,1,n,l,r); else printf("%lld\n",query(1,1,n,l,r)); } }