给定序列,两种操作,区间每个数乘v,求区间积(对1e9+7取模)后的欧拉函数。
序列每个数以及v都不超过300。
- 首先bd得到欧拉函数的计算公式:
- 首先序列每个数不超过300,v也不超过300,300以内的质数很少,打表发现只有62个,这个信息显然很有用。
- 区间欧拉函数就是区间积,很明显可以用线段树维护 。把欧拉函数看成两部分相乘,一个是x本身,一个是x包含的所有质因子项的乘积,我们线段树维护的节点有两个信息,一个是区间乘积,一个是区间包含的质因子,后者用状压,用一个longlong的变量保存。
- 质因子项我们可以预处理+逆元得到,用
f[i]
表示第i个质数p[i]的(p[i]-1)/p[i] % mod
- 能否不存质因子,最后求出区间乘积之后再求质因子呢?不行,因为x在乘完之后要取模,取模后求的质因子肯定是错了的,所以我们维护区间积的同时,再维护区间所有数的质因子。
区间合并:
其余细节见注释。
#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ll long long
#define ull unsigned long long
#define pll pair<ll,ll>
#define re register
#define lc rt<<1
#define rc rt<<1|1
const int maxn = 4e5+10;
const int mx = 40;
const ll mod = 1e9+7;
const ll inf = 34359738370;
const ll INF = 1e9+7;
const double pi = acos(-1.0);
//给定序列 初始值不超过300 区间乘 查询区间积的欧拉函数
//维护区间积以及区间积的质因子状态
//用线段树 欧拉函数根据公式计算 由于涉及取模 需要记录质因子 预处理每个质因子乘积项
ll tree[maxn<<2],has[maxn<<2];//区间积 区间质因子状态
ll tag1[maxn<<2],tag2[maxn<<2];//乘积的懒标记 乘积质因子懒标记
ll sta[305];//数i的质因子压缩状态 右边第一位是第一个质数2
int pri[65],tot=0;//质数表
bool vis[305];
ll f[65];//预处理质因子项
ll inv[305];//逆元
void pre()
{
for(int i=2;i<=300;i++)
{
if(!vis[i]) pri[++tot]=i;
for(int j=1;j<=tot && i*pri[j]<=300;j++)
{
vis[i*pri[j]]=1;
if(i % pri[j] == 0) break;
}
}
for(int i=2;i<=300;i++)
{
//i的每个质因子都压缩成二进制状态 右边第一个位置对应质数2
for(int j=1;j<=tot;j++)
{
if(i % pri[j] == 0) sta[i] |= 1ll<<(j-1);//注意是1ll
}
}
inv[1]=1;
for(int i=2;i<=300;i++)
inv[i]=mod-1ll*(mod/i)*inv[mod%i]%mod;
for(int i=1;i<=tot;i++)
f[i]=inv[pri[i]]*(pri[i]-1)%mod;//预处理 方便计算欧拉函数
}
inline void pushup(int rt) //合并区间信息
{
tree[rt]=tree[lc]*tree[rc]%mod;
has[rt]=has[lc] | has[rc];
}
void build(int rt,int l,int r)
{
tag1[rt]=1;//乘标记
tag2[rt]=0;//质因子标记 这个区间乘的数包括哪些质因子 状压
if(l == r)
{
scanf("%d",&tree[rt]);
has[rt]=sta[tree[rt]];
return ;
}
int mid=l+r>>1;
build(lc,l,mid);
build(rc,mid+1,r);
pushup(rt);
}
inline ll qpow(ll a,int b)
{
ll ans=1;
for(; b ; b>>=1 , a=a*a%mod )
if(b&1) ans=ans*a%mod;
return ans;
}
inline void change(int rt,int len,ll v,ll y) //区间每个数乘v 更新区间乘积以及质因子状态
{
tree[rt]=qpow(v,len)*tree[rt]%mod;
has[rt] |= y;
tag1[rt]=tag1[rt]*v%mod;
tag2[rt] |= y;
}
inline void pushdown(int rt,int l,int r)
{
if(!tag2[rt]) return ;//这个区间没有乘过
int mid=l+r>>1;
change(lc,mid-l+1,tag1[rt],tag2[rt]);
change(rc,r-mid,tag1[rt],tag2[rt]);
//下传完 标记初始化
tag1[rt]=1;
tag2[rt]=0;
}
inline void updata(int rt,int l,int r,int v,int vl,int vr)//[vl,vr]每个数乘v
{
if(vl > r || vr < l ) return ;
if(vl<=l && r<=vr)
{
change(rt,r-l+1,v,sta[v]);
return ;
}
int mid=l+r>>1;
pushdown(rt,l,r);
updata(lc,l,mid,v,vl,vr);
updata(rc,mid+1,r,v,vl,vr);
pushup(rt);
}
inline pll hb(pll a ,pll b)
{
a.first=a.first*b.first%mod;
a.second |= b.second;
return a;
}
//返回[vl,vr]区间积以及质因子状态
inline pll query(int rt,int l,int r,int vl,int vr)
{
if(vl<=l && r<=vr)
{
return make_pair(tree[rt],has[rt]);
}
int mid=l+r>>1;
pushdown(rt,l,r);
if(vr <= mid) return query(lc,l,mid,vl,vr);
else if(vl > mid) return query(rc,mid+1,r,vl,vr);
return hb(query(lc,l,mid,vl,vr),query(rc,mid+1,r,vl,vr));
}
int n,m;
int main()
{
pre();//预处理别忘了写
scanf("%d %d",&n,&m);
build(1,1,n);
char op[15];
int l,r,v;
while(m--)
{
scanf("%s %d %d",op,&l,&r);
if(op[0] == 'T')
{
pll t=query(1,1,n,l,r);
for(int i=1;i<=tot;i++)
{
if(t.second & (1ll<<(i-1)))
t.first = (t.first*f[i])%mod;
}
printf("%lld\n",t.first);
}
else
{
scanf("%d",&v);
updata(1,1,n,v,l,r);
}
}
return 0;
}