1.维护一个序列,支持两个操作:
(1)0 l r w
[
l
,
r
]
[l,r]
[l,r]区间乘w
(2)1 l r 询问
[
l
,
r
]
[l,r]
[l,r]区间欧拉函数和
2.思路:考虑欧拉函数的性质:
如
果
p
是
质
数
,
p
∣
n
且
p
∣
n
2
,
则
φ
(
n
)
=
φ
(
n
/
p
)
∗
p
如果p是质数,p|n且p|n^2,则\varphi(n)=\varphi(n/p)*p
如果p是质数,p∣n且p∣n2,则φ(n)=φ(n/p)∗p
如
果
p
是
质
数
,
p
∣
n
且
p
∤
n
2
,
则
φ
(
n
)
=
φ
(
n
/
p
)
∗
(
p
−
1
)
如果p是质数,p|n且p\nmid n^2,则\varphi(n)=\varphi(n/p)*(p-1)
如果p是质数,p∣n且p∤n2,则φ(n)=φ(n/p)∗(p−1)
我们考虑对要乘的w分解质因数,如果对于p,区间符合第一条(就是出现过这个质因子)就直接按正常线段树搞,如果不符合(没出现过质因子p)就递归到叶子节点改,最后在push_up上来。可以看到,递归到子节点的操作是有限的。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=800010,mod=998244353;
ll sum[N],tag[N][30],mlaz[N];
ll primes[N],cnt;
bool st[N];
void get_primes(ll n)
{
for(int i=2;i<=n;i++)
{
if(!st[i])primes[cnt++]=i;
for(int j=0;primes[j]<=n/i;j++)
{
st[primes[j]*i]=true;
if(i%primes[j]==0) break;
}
}
}
void pushup(ll rt)
{
sum[rt]=(sum[rt<<1]+sum[rt<<1|1])%mod;
for(int i=0;i<cnt;i++)
{
if(tag[rt<<1][i]&&tag[rt<<1|1][i]) tag[rt][i]=1;
else tag[rt][i]=0;
}
}
void pushdown(ll rt)
{
if(mlaz[rt]!=1)
{
sum[rt<<1]=(sum[rt<<1]*mlaz[rt])%mod;
sum[rt<<1|1]=(sum[rt<<1|1]*mlaz[rt])%mod;
mlaz[rt<<1]=(mlaz[rt<<1]*mlaz[rt])%mod;
mlaz[rt<<1|1]=(mlaz[rt<<1|1]*mlaz[rt])%mod;
mlaz[rt]=1;
}
}
void build(ll l,ll r,ll rt)
{
mlaz[rt]=1;
for(int i=0;i<cnt;i++) tag[rt][i]=0;
if(l==r)
{
ll x;
scanf("%lld",&x);
int res=x;
for(int i=0;i<cnt;i++)
{
ll y=primes[i];
if (x%y==0)
{
tag[rt][i]=1;
res=res/y*(y-1);
}
}
sum[rt]=res;
return ;
}
ll m=(l+r)>>1;
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
pushup(rt);
}
void update(ll L,ll R,ll c,ll l,ll r,ll rt)
{
if(L<=l&&r<=R)
{
if(tag[rt][c]==1)
{
mlaz[rt]=(mlaz[rt]*primes[c])%mod;
sum[rt]=(sum[rt]*primes[c])%mod;
}
else
{
tag[rt][c]=1;
if(l==r)
{
sum[rt]=(sum[rt]*(primes[c]-1))%mod;
return ;
}
pushdown(rt);
ll m=(l+r) >> 1;
if(L<=m) update(L,R,c,l,m,rt << 1);
if(m<R) update(L,R,c,m+1,r,rt<<1|1);
pushup(rt);
}
return ;
}
pushdown(rt);
ll m=(l+r) >> 1;
if(L<=m) update(L,R,c,l,m,rt << 1);
if(m<R) update(L,R,c,m+1,r,rt << 1|1);
pushup(rt);
}
ll query(ll L,ll R,ll l,ll r,ll rt)
{
if(L<=l&&r<=R)
{
return sum[rt]%mod;
}
pushdown(rt);
ll m=(l+r) >> 1;
ll ret=0;
if(L <= m)
{
ret+=query(L,R,l,m,rt<<1);
ret%=mod;
}
if(m < R)
{
ret+=query(L,R,m+1,r,rt<<1|1);
ret%=mod;
}
return ret%mod;
}
int main()
{
get_primes(100);
ll n,q;
scanf("%lld%lld",&n,&q);
build(1,n,1);
while(q--)
{
ll op;
scanf("%lld",&op);
if(op==0)
{
ll a,b,w;
scanf("%lld%lld%lld",&a,&b,&w);
for(int i=0;i<cnt;i++)
{
ll y=primes[i];
while(w%y==0)
{
update(a,b,i,1,n,1);
w/=y;
}
}
}
else if(op==1)
{
ll a,b;
scanf("%lld%lld",&a,&b);
printf("%lld\n",query(a,b,1,n,1)%mod);
}
}
return 0;
}