Ynoi竟然出了个奈芙莲的题,少见啊
Ynoi竟然有个不卡常的题,更少见了
这道题其实思路非常好想,本质上就是一个暴力,但是要用到拓展欧拉定理,是什么呢?
a c ≡ { a c , c < ϕ ( p ) a c m o d ϕ ( p ) + ϕ ( p ) , c ≥ ϕ ( p ) m o d p a^c\equiv\begin{cases}a^c,c<\phi(p) \\ a^{c\bmod \phi(p)+\phi(p)},c\geq\phi(p)\end{cases} \bmod p ac≡{ac,c<ϕ(p)acmodϕ(p)+ϕ(p),c≥ϕ(p)modp
这个东西有什么用呢?做过这道题的就应该很明显了
我们观察 ϕ ( p ) \phi(p) ϕ(p)一定 ≤ n 2 \leq\frac{n}{2} ≤2n,那么也就是说最多经过 log p \log p logp次之后,我们的 ϕ ( ϕ ( ϕ ( . . . ϕ ( p ) ) ) ) = 1 \phi(\phi(\phi(...\phi(p))))=1 ϕ(ϕ(ϕ(...ϕ(p))))=1,这个时候根据我们上面的拓展欧拉定理,我们就不需要继续算剩下的部分了,因为剩下的部分算完肯定是 1 1 1
也就是说我们对于每次查询,从 x x x暴力往右扫,每次都让 p = ϕ ( p ) p=\phi(p) p=ϕ(p),那么经过一定次数之后剩下的就不用管了,然后我们再回溯更新这一部分的答案就可以
因为有区间加之类的操作,所以我们用树状数组维护差分,就可以求出每一个位置上的数是多少了
其实这题根这道题也挺像的吧,但是上帝那道题的数据比较水,我们会发现,当我们的 c < ϕ ( p ) c<\phi(p) c<ϕ(p)的时候,我们是不能 + ϕ ( p ) +\phi(p) +ϕ(p)的,这个东西就非常的恶心,我们需要特判一下
特判的方法也比较简单,我们在做快速幂的过程中,每次计算完先不取模,如果发现比 p p p大, f l a g = 1 flag=1 flag=1,然后再取模,做完快速幂之后再判断如果 f l a g = 1 flag=1 flag=1,那么再加上 ϕ ( p ) \phi(p) ϕ(p)
快速幂大概是这个样子的
int Qpow(int base,int ind,int mod){
int res=1;
flag=false;
if(base>=mod)flag=true,base%=mod;
while(ind){
if(ind&1){
res=res*base;
if(res>=mod)flag=true,res%=mod;
}
base=base*base;
if(base>=mod)flag=true,base%=mod;
ind>>=1;
}
return res;
}
好长啊
查询的时候其实是不用写 d f s dfs dfs的,我们可以直接手动模拟栈,先把要求的部分的模数都预处理出来,然后从右往左算就可以
else{
int now=x;
modn[x]=p;
p=phi[p];
while(p>1&&now<y){
modn[++now]=p;
p=phi[p];
}
int res=1;
_Rep(i,now,x){
res=Qpow(ask(i),res,modn[i]);
if(flag)res+=modn[i];
}
printf("%lld\n",res%modn[x]);
}
由于 p ≤ 2 × 1 0 7 p\leq 2\times10^7 p≤2×107,所以我们要线性筛,然后这题空间500MB,放心的开longlong吧。
其他细节没什么了,只要不像我一样线性筛出一堆锅就好
然后是这题的复杂度,我们发现,每次询问我们最多查询 log p \log p logp次,每次需要 log n \log n logn的时间,然后算快速幂需要 log p \log p logp次的时间,所以这题的总复杂度应该是 O ( m log n log 2 p ) O(m\log n\log^2 p) O(mlognlog2p)
毒瘤的lxl这题仁慈的开了3s时限
代码:
#include <bits/stdc++.h>
using namespace std;
# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)
typedef long long ll;
const int N=5e5+5;
const int M=2e7+5;
template<typename T> void read(T &x){
x=0;int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
x*=f;
}
# define int long long
int n,m;
int a[N],bit[N];
int phi[M],prime[N*10],tot;
int modn[N];
bool isp[M],flag;
int lowbit(int o){
return o&-o;
}
void add(int o,int x){
for(;o<=n;o+=lowbit(o))bit[o]+=x;
}
int ask(int o){
int res=0;
for(;o;o-=lowbit(o))res+=bit[o];
return res;
}
void init()
{
phi[1]=1;
for(int i=2;i<=2e7;i++){
if(isp[i])prime[++tot]=i,phi[i]=i-1;
for(int j=1;j<=tot&&i*prime[j]<=2e7;j++){
isp[i*prime[j]]=false;
if(i%prime[j]==0){
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
else phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}
}
int Qpow(int base,int ind,int mod){
int res=1;
flag=false;
if(base>=mod)flag=true,base%=mod;
while(ind){
if(ind&1){
res=res*base;
if(res>=mod)flag=true,res%=mod;
}
base=base*base;
if(base>=mod)flag=true,base%=mod;
ind>>=1;
}
return res;
}
signed main()
{
memset(isp,true,sizeof(isp));
read(n),read(m);
Rep(i,1,n)read(a[i]);
Rep(i,1,n)add(i,a[i]-a[i-1]);
init();
Rep(i,1,m){
int opt,x,y,p;
read(opt),read(x),read(y),read(p);
if(opt==1){
add(x,p);
add(y+1,-p);
}
else{
int now=x;
modn[x]=p;
p=phi[p];
while(p>1&&now<y){
modn[++now]=p;
p=phi[p];
}
int res=1;
_Rep(i,now,x){
res=Qpow(ask(i),res,modn[i]);
if(flag)res+=modn[i];
}
printf("%lld\n",res%modn[x]);
}
}
return 0;
}
因为这题是Ynoi,所以来说点没用的
奈芙莲其实戏份挺少的,因为第四卷没有拍嘛
虽然她没有爱尔和珂朵莉的美貌
但是她达成了一件珂朵莉都没有达成的事,抱着威廉睡觉


而且,她是不是也算是在最后一刻绽放呢?
她难道不也是最幸福的女孩子吗?


本文解析了一道罕见的Ynoi奈芙莲题目,介绍了使用拓展欧拉定理解决暴力算法问题的思路。通过分析ϕ(p)phi(p)ϕ(p)的性质,提出了一种基于树状数组维护差分的解决方案,详细阐述了快速幂运算的实现,并给出了代码示例。
896

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



