qwq被Creed吊锤
sro Creed orz
一道好题。
首先考虑该如何求
φ
(
i
)
\varphi(i)
φ(i),由于这个数据范围,尤其是权值的范围,就让人感觉可能存在一些突破的机会
由于欧拉函数是一个积性函数,那么如果存在两个互质的两个数 a 和 b a和b a和b, φ ( a ∗ b ) = φ ( a ) × φ ( b ) \varphi(a*b) = \varphi(a) \times \varphi(b) φ(a∗b)=φ(a)×φ(b)
由于质数的欧拉函数值比较特别,并且值域的范围也是比较小,不妨
考虑对于一个质数 p p p的 k k k次方,应该如何计算他的欧拉函数值
那么我们比较容易发现,只有 p p p的倍数才不是互质的,那么我们不让用总的数的个数,减去 1 到 p k 1到p^k 1到pk中 p p p的倍数,也就说说 φ ( p k ) = p k − p k − 1 = p k − 1 × ( p − 1 ) = p k × p − 1 p \varphi(p^k) = p^k - p^{k-1} = p^{k-1} \times (p-1) = p^k \times \frac{p-1}{p} φ(pk)=pk−pk−1=pk−1×(p−1)=pk×pp−1
那么由于积性函数,我们可以算出来每一个质因子的对应的欧拉函数的值,然后直接相乘,前一项的乘积就是所有 a i a_i ai的乘积。
后面一项,我们发现质数的数量并不是很多,而且对于一个质因子,我们只需要关心他是不是存在,我们可以直接把状态压缩成一个 l o n g l o n g long long longlong来表示。
那么维护一个区间修改,区间询问的值,可以直接用线段树来实现
通过一些奇奇怪怪的方法 u p up up
void up(int root)
{
f[root].prob = f[2*root].prob*f[2*root+1].prob%mod;
f[root].ymh = f[2*root].ymh | f[2*root+1].ymh;
}
有一个需要注意的细节,就是我们进行乘法的时候,下传标记的时候呢,对于一个区间来说,我们要乘一个类似于乘积的形式。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk make_pair
#define ll long long
#define int long long
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
const int maxn = 1e6+1e2;
const int mod = 1e9+7;
struct Node{
int prob;
int ymh;
};
Node f[4*maxn];
int n,m;
int prime[maxn];
int ch[4*maxn];
int tag[4*maxn];
int a[maxn];
int val[maxn];
int qsm(int i,int j)
{
int ans=1;
while (j)
{
if (j&1) ans=ans*i%mod;
i=i*i%mod;
j>>=1;
}
return ans;
}
void up(int root)
{
f[root].prob = f[2*root].prob*f[2*root+1].prob%mod;
f[root].ymh = f[2*root].ymh | f[2*root+1].ymh;
}
void pushdown(int root,int l,int r)
{
if (ch[root]!=1)
{
ch[2*root]=ch[2*root]*ch[root]%mod;
ch[2*root+1]=ch[2*root+1]*ch[root]%mod;
int mid = l+r >> 1;
f[2*root].prob=f[2*root].prob*qsm(ch[root],mid-l+1)%mod;
f[2*root+1].prob=f[2*root+1].prob*qsm(ch[root],r-(mid+1)+1)%mod;
ch[root]=1;
}
if (tag[root])
{
tag[2*root]|=tag[root];
tag[2*root+1]|=tag[root];
f[2*root].ymh|=tag[root];
f[2*root+1].ymh|=tag[root];
tag[root]=0;
}
}
void build(int root,int l,int r)
{
ch[root]=1;
tag[root]=0;
if (l==r)
{
f[root].prob = a[l];
f[root].ymh = val[l];
return;
}
int mid = l+r >> 1;
build(2*root,l,mid);
build(2*root+1,mid+1,r);
up(root);
}
void update(int root,int l,int r,int x,int y,int p)
{
if (x<=l && r<=y)
{
p%=mod;
f[root].prob = f[root].prob*qsm(p,r-l+1)%mod;
ch[root]=ch[root]*p%mod;
return;
}
pushdown(root,l,r);
int mid = l+r >> 1;
if (x<=mid) update(2*root,l,mid,x,y,p);
if (y>mid) update(2*root+1,mid+1,r,x,y,p);
up(root);
}
void modify(int root,int l,int r,int x,int y,int p)
{
if (x<=l && r<=y)
{
f[root].ymh|=p;
tag[root]|=p;
return;
}
pushdown(root,l,r);
int mid = l+r >> 1;
if (x<=mid) modify(2*root,l,mid,x,y,p);
if (y>mid) modify(2*root+1,mid+1,r,x,y,p);
up(root);
}
Node query(int root,int l,int r,int x,int y)
{
if (x<=l && r<=y) return f[root];
pushdown(root,l,r);
int mid = l+r >> 1;
Node ans,ans1,ans2;
ans=ans1=ans2=(Node){1,0};
if (x<=mid) ans1 = query(2*root,l,mid,x,y);
if (y>mid) ans2 = query(2*root+1,mid+1,r,x,y);
ans.prob = ans1.prob*ans2.prob%mod;
ans.ymh = ans1.ymh | ans2.ymh;
return ans;
}
int q;
bool check(int x)
{
for (int i=2;i*i<=x;i++)
{
if (x%i==0) return false;
}
return true;
}
int tot;
void init()
{
for (int i=2;i<=300;i++)
if (check(i))
prime[++tot]=i;
}
int szh[maxn],inv[maxn];
signed main()
{
n=read(),q=read();
init();
for (int i=1;i<=n;i++) a[i]=read();
for (int i=1;i<=300;i++)
{
for (int j=1;j<=tot;j++)
{
if (i%prime[j]==0) szh[i]|=(1ll <<(j-1));
}
}
for (int i=1;i<=n;i++) val[i]=szh[a[i]];
for (int i=1;i<=300;i++) inv[i]=qsm(i,mod-2);
build(1,1,n);
for (int i=1;i<=q;i++)
{
char s[20];
scanf("%s",s+1);
if (s[1]=='M')
{
int l=read(),r=read(),x=read();
update(1,1,n,l,r,x);
modify(1,1,n,l,r,szh[x]);
}
else
{
int l=read(),r=read();
Node now = query(1,1,n,l,r);
int ans=now.prob;
for (int j=1;j<=tot;j++)
{
if ((1ll<<(j-1)) & now.ymh)
{
ans=ans*(prime[j]-1)%mod*inv[prime[j]]%mod;
}
}
cout<<ans<<"\n";
}
}
return 0;
}