Time Limit: 40 Sec
Memory Limit: 512 MB
Description
Informatikverbindetdichundmich.
信息将你我连结。B君希望以维护一个长度为n的数组,这个数组的下标为从1到n的正整数。一共有m个操作,可以
分为两种:0 l r表示将第l个到第r个数(al,al+1,…,ar)中的每一个数ai替换为c^ai,即c的ai次方,其中c是
输入的一个常数,也就是执行赋值ai=c^ai1 l r求第l个到第r个数的和,也就是输出:sigma(ai),l<=i<=rai因为
这个结果可能会很大,所以你只需要输出结果mod p的值即可。
Input
第一行有三个整数n,m,p,c,所有整数含义见问题描述。
接下来一行n个整数,表示a数组的初始值。
接下来m行,每行三个整数,其中第一个整数表示了操作的类型。
如果是0的话,表示这是一个修改操作,操作的参数为l,r。
如果是1的话,表示这是一个询问操作,操作的参数为l,r。
1 ≤ n ≤ 50000, 1 ≤ m ≤ 50000, 1 ≤ p ≤ 100000000, 0 < c <p, 0 ≤ ai < p
Output
对于每个询问操作,输出一行,包括一个整数表示答案mod p的值。
题目分析
如果做过这两题(特别是前一道)的话再看这题会容易很多
BZOJ3884 上帝与集合的正确用法
洛谷P4145 上帝造题的七分钟2
首先这题最关键的突破口在于
- 对于同一个位置的数,执行一定次数的修改操作后这个数就不会再变化,而这个次数是对数级的
每次修改后产生的数可以看作
c
c
c
.
.
.
a
c^{c^{c^{...^{a}}}}
ccc...a,这与BZOJ3884是一样的形式
可以根据扩展欧拉定理
a
,
p
∈
Z
a,p\in Z
a,p∈Z,则
a
b
=
a^b=
ab=
{
a
b
,
b
<
φ
(
p
)
a
b
m
o
d
  
φ
(
p
)
+
φ
(
p
)
,
b
>
=
φ
(
p
)
\left\{\begin{aligned}a^b,b<\varphi(p)\\ a^{b\mod\varphi(p)+\varphi(p) },b>=\varphi(p)\end{aligned}\right.
{ab,b<φ(p)abmodφ(p)+φ(p),b>=φ(p)
m
o
d
  
p
\mod p
modp递归处理
递归的边界是模数
p
=
=
1
p==1
p==1
也就是说一个数需要处理的修改次数最多只有 从
p
p
p开始不断进行
p
=
φ
(
p
)
p=\varphi(p)
p=φ(p)直到
p
=
=
1
p==1
p==1的次数
根据欧拉phi函数计算公式
φ
(
N
)
=
N
∗
∏
i
=
1
k
p
i
−
1
p
i
\varphi(N)=N*\prod_{i=1}^k\frac{p_i-1}{p_i}
φ(N)=N∗∏i=1kpipi−1
若
N
N
N为偶数,它一定存在质因子
2
2
2,计算式中一定存在
×
1
2
\times\frac{1}{2}
×21项
若
N
N
N为奇数,因为质数(除2以外)一定是奇数,则
p
i
−
1
p_i-1
pi−1一定是偶数,所以
φ
(
N
)
\varphi(N)
φ(N)一定是偶数
所以上述的迭代次数一定是对数级的
我们记上述最大修改次数为
c
n
t
cnt
cnt(可以预处理)
既然每个数被修改次数是对数级的,那么可以仿照 洛谷P4145 上帝造题的七分钟2]
用线段树维护区间和、区间最小修改次数
m
i
mi
mi
每次修改直接进入到最低层修改,若发现某个区间
m
i
>
=
c
n
t
mi>=cnt
mi>=cnt,则可以不进入该区间修改
这样每次修改递归
l
o
g
p
logp
logp层,每层快速幂计算
O
(
l
o
g
p
)
O(logp)
O(logp),每个数最多被修改
l
o
g
p
logp
logp次
复杂度
O
(
n
l
o
g
n
∗
l
o
g
3
p
)
O(nlogn*log^3p)
O(nlogn∗log3p) 只能信仰过吧
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long lt;
lt read()
{
lt f=1,x=0;
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return f*x;
}
const int maxn=50010;
lt n,m,mod,c;
lt a[maxn],sum[maxn<<2],mi[maxn<<2];
lt phi[maxn],cnt,rem;
lt qpow(lt aa,lt k,lt mm)
{
lt res=1; rem=0;
while(k>0){
if(k&1) res=res*aa;
aa=(aa*aa)%mm; k>>=1;
if(res>=mm) rem=1,res%=mm;
}
return res;
}
void build(int s,int t,int p)
{
if(s==t){ sum[p]=a[s]%mod; return;}
int mid=s+t>>1;
build(s,mid,p<<1); build(mid+1,t,p<<1|1);
sum[p]=(sum[p<<1]+sum[p<<1|1])%mod;
}
lt Phi(lt x)
{
lt res=x;
for(int i=2;i*i<=x;++i)
if(x%i==0)
{
res=res/i*(i-1);
while(x%i==0) x/=i;
}
if(x>1) return res/x*(x-1);
else return res;
}
void work(lt x)
{
phi[0]=x;
while(x!=1)
{
x=Phi(x);
phi[++cnt]=x;
}
phi[++cnt]=1;
}
lt qsum(int ll,int rr,int s,int t,int p)
{
if(ll<=s&&t<=rr) return sum[p]%mod;
int mid=s+t>>1; lt ans=0;
if(ll<=mid) ans=(ans+qsum(ll,rr,s,mid,p<<1))%mod;
if(rr>mid) ans=(ans+qsum(ll,rr,mid+1,t,p<<1|1))%mod;
return ans;
}
lt calc(lt val,int lev,int k)
{
if(lev==0){
if(val>=phi[k]) rem=1;
return val;
}
lt tt=calc(val,lev-1,k+1);
tt=rem?tt%phi[k+1]+phi[k+1]:tt;
return qpow(c,tt,phi[k]);
}
void update(int ll,int rr,int s,int t,int p)
{
if(mi[p]>=cnt) return;
if(s==t){
++mi[p];
sum[p]=calc(a[s],mi[p],0);
return;
}
int mid=s+t>>1;
if(ll<=mid&&mi[p<<1]<cnt) update(ll,rr,s,mid,p<<1);
if(rr>mid&&mi[p<<1|1]<cnt) update(ll,rr,mid+1,t,p<<1|1);
sum[p]=(sum[p<<1]+sum[p<<1|1])%mod;
mi[p]=min(mi[p<<1],mi[p<<1|1]);
}
int main()
{
n=read();m=read();mod=read();c=read();
for(int i=1;i<=n;++i) a[i]=read();
build(1,n,1);
work(mod);
while(m--)
{
int opt=read(),ll=read(),rr=read();
if(opt==0) update(ll,rr,1,n,1);
else if(opt==1) printf("%lld\n",qsum(ll,rr,1,n,1));
}
return 0;
}
由于三个log的复杂度不是很可观,所以考虑预处理c的幂次方
直接开数组存不下,可以用
p
o
w
1
[
i
]
pow1[i]
pow1[i]表示
c
i
c^i
ci,
p
o
w
2
[
i
]
pow2[i]
pow2[i]表示
c
i
∗
10000
c^{i*10000}
ci∗10000
则
c
k
=
p
o
w
1
[
k
m
o
d
  
10000
]
∗
p
o
w
[
k
/
10000
]
c^k=pow1[k\mod10000]*pow[k/10000]
ck=pow1[kmod10000]∗pow[k/10000]
预处理的时候顺便记录下对应扩展欧拉定理哪一条
总复杂度
O
(
n
l
o
g
n
∗
l
o
g
2
p
)
O(nlogn*log^2p)
O(nlogn∗log2p)
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long lt;
lt read()
{
lt f=1,x=0;
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return f*x;
}
const int maxn=50010;
lt n,m,mod,c;
lt a[maxn],sum[maxn<<2],mi[maxn<<2];
lt phi[maxn],cnt,judge;
lt tpow[maxn][110][2],rem[maxn][110][2];
void build(int s,int t,int p)
{
if(s==t){ sum[p]=a[s]%mod; return;}
int mid=s+t>>1;
build(s,mid,p<<1); build(mid+1,t,p<<1|1);
sum[p]=(sum[p<<1]+sum[p<<1|1])%mod;
}
lt Phi(lt x)
{
lt res=x;
for(int i=2;i*i<=x;++i)
if(x%i==0)
{
res=res/i*(i-1);
while(x%i==0) x/=i;
}
if(x>1) return res/x*(x-1);
else return res;
}
void work(lt x)
{
phi[0]=x;
while(x!=1)
{
x=Phi(x);
phi[++cnt]=x;
}
phi[++cnt]=1;
for(int i=0;i<=cnt;++i)
{
tpow[0][i][0]=1;
for(int j=1;j<=10000;++j)
{
tpow[j][i][0]=tpow[j-1][i][0]*c;
if(tpow[j][i][0]>=phi[i]) tpow[j][i][0]%=phi[i],rem[j][i][0]=1;
rem[j][i][0]|=rem[j-1][i][0];
}
}
for(int i=0;i<=cnt;++i)
{
tpow[0][i][1]=1;
rem[1][i][1]=rem[10000][i][0];
for(int j=1;j<=10000;++j)
{
tpow[j][i][1]=tpow[j-1][i][1]*tpow[10000][i][0];
if(tpow[j][i][1]>=phi[i]) tpow[j][i][1]%=phi[i],rem[j][i][1]=1;
rem[j][i][1]|=rem[j-1][i][1];
}
}
}
lt qsum(int ll,int rr,int s,int t,int p)
{
if(ll<=s&&t<=rr) return sum[p]%mod;
int mid=s+t>>1; lt ans=0;
if(ll<=mid) ans=(ans+qsum(ll,rr,s,mid,p<<1))%mod;
if(rr>mid) ans=(ans+qsum(ll,rr,mid+1,t,p<<1|1))%mod;
return ans;
}
lt qpow(lt k,lt id)
{
judge=0;
lt res=tpow[k%10000][id][0]*tpow[k/10000][id][1];
if(res>=phi[id]) res=res%phi[id],judge=1;
judge|=rem[k%10000][id][0]|rem[k/10000][id][1];
return res;
}
lt calc(lt val,int lev,int k)
{
judge=0;
if(lev==0){
if(val>=phi[k]) judge=1,val%=phi[k];
return val;
}
lt tt=calc(val,lev-1,k+1);
if(judge) tt=tt%phi[k+1]+phi[k+1];
return qpow(tt,k);
}
void update(int ll,int rr,int s,int t,int p)
{
if(mi[p]>=cnt) return;
if(s==t){
++mi[p];
sum[p]=calc(a[s],mi[p],0);
return;
}
int mid=s+t>>1;
if(ll<=mid&&mi[p<<1]<cnt) update(ll,rr,s,mid,p<<1);
if(rr>mid&&mi[p<<1|1]<cnt) update(ll,rr,mid+1,t,p<<1|1);
sum[p]=(sum[p<<1]+sum[p<<1|1])%mod;
mi[p]=min(mi[p<<1],mi[p<<1|1]);
}
int main()
{
n=read();m=read();mod=read();c=read();
for(int i=1;i<=n;++i) a[i]=read();
build(1,n,1);
work(mod);
while(m--)
{
int opt=read(),ll=read(),rr=read();
if(opt==0) update(ll,rr,1,n,1);
else if(opt==1) printf("%lld\n",qsum(ll,rr,1,n,1));
}
return 0;
}