题干描述
题目描述
给定一个长度为 𝑛 的数组,第 𝑖 项为𝑎i,你需要支持以下操作: 1,给定l,r,𝑥,将区间 [𝑙,𝑟] 中所有元素乘 𝑥。 2. 给定 𝑙,𝑟,查询区间 [𝑙,𝑟] 中所有元素之积的欧拉函数值,因为结果可能很大,你只需要输出答案对 10^9+ 7 取模的结果。 你共需进行 𝑞 次操作。
输入输出格式
输入:
第一行两个整数 𝑛,𝑞,分别代表数组的长度与操作的次数。 第二行 𝑛 个整数,代表数组内初始元素。 接下来 𝑞 行形式为 1 l r x 或 2 l r,含义如上所述。
输出:
输出 𝑞 行,每行一个整数表示对应询问的答案,你需要正确回答所有询问来获取该测试点的分数。
样例
输入数据1
4 4
2 4 6 7
2 1 4
1 2 3 3
1 3 4 4
2 3 4
输出数据1
96
576
输入数据2
5 7
252 292 6 122 8
2 4 5
2 1 5
1 2 4 96
2 4 5
2 2 5
1 3 5 175
2 3 4
输出数据2
480
119439360
30720
223615137
448639678
数据范围
对于100%的数据 :
题解
分析
首先,我们先要知道数论的一些基本知识
1. (欧拉函数)
2. (费马小定理)
这部分证明去问隔壁数竞吧,跟我们信竞没关系......
现在, 我们要通过公式1, 求出区间积的欧拉函数值
公式由两部分组成: 与
对于n
我们知道, 初始值与后乘的数皆, 而会对产生贡献的只会是的质因数, 小于300的质数只有62个
所以, 我们可以利用一个62位的来记录第个质因数是否在中出现过, 利用线段树维护即可(乘积 和 是否出现的质数 都要维护)
对于1-(1/p)
因为取模的原因, 我们需要预处理出所有质因数的逆元
利用费马小定理,可以推导出
等价于
同时, 也等价于
在的条件下,
最后把他们乘到一起, 再取模即可
优化
1.快速幂
2.预处理出300以内质数逆元, 300以内所有数包含的质因数
3.快读快写
4.祈求老师把时间开大一些
AC Code
#include <bits/stdc++.h>
using namespace std;
#define ls(x) x<<1
#define rs(x) x<<1|1
#define int long long
#define N 100086
const int mod=1e9 + 7;
int n,q;
int a[N],prim[70],vis[310];
int tot=0;
int mul[N<<2];
bitset<70> tag[N<<2];//tag表示当前区间(或节点)乘积是否含某质数
bitset<70> lzt[N<<2];//tag的"lzt"
bitset<70> y_num[310];
int to[310],in_e[70];
int lzm[N<<2];
void read(int &x) {
char c;
do {
c = getchar();
} while (c == ' ' || c == '\n');
x = 0;
int w = 1;
if (c == '-') {
w = -1;
c = getchar();
}
do {
x = (x << 1) + (x << 3) + c - '0';
c = getchar();
} while (c != ' ' && c != '\n');
x *= w;
}
inline void wt(int x){
if(x<0) putchar('-'),x=-x;
if(x>9) wt(x/10);
putchar(x%10+'0');return;
}
int qpow(int a,int b){
int ans=1;
while(b!=0){
if(b%2==1){
ans=ans%mod*a%mod;
ans%=mod;
}
a=a%mod*a%mod;
b>>=1;
}
return ans;
}
void init(){
for(int x=2;x<=300;x++){
if(!vis[x]){
prim[++tot]=x;
to[x]=tot;
}
for(int y=1;x*prim[y]<=300;y++){
vis[x*prim[y]]=1;
if(x%prim[y]==0){
break;
}
}
}
for(int x=2;x<=305;x++){
int xx=x;
for(int y=1;y<=62;y++){
if(xx%prim[y]==0){
while(xx%prim[y]==0){
xx/=prim[y];
}
y_num[x].set(y);
}
}
}
for(int x=1;x<=tot;x++){
in_e[x]=qpow(prim[x],mod-2);
}
}
void push_up(int now){
tag[now]= tag[ls(now)] | tag[rs(now)];
mul[now]= mul[ls(now)] * mul[rs(now)]%mod;
mul[now]%=mod;
}
void build(int now,int l,int r){
lzm[now]=1;
lzt[now].reset();
if(l==r){
mul[now]=a[l];
tag[now]=y_num[a[l]];
return;
}
mul[now]=1;
int mid=(l+r)>>1;
build(ls(now),l,mid);
build(rs(now),mid+1,r);
push_up(now);
// cout << l << ' ' << r << ' ' << tag[now] << '\n';
}
void push_down(int u,int l,int r){
int mid=(l+r)>>1;
if(l!=r){
lzt[ls(u)]|=lzt[u];lzt[rs(u)]|=lzt[u];
tag[ls(u)]|=lzt[u];tag[rs(u)]|=lzt[u];
lzm[ls(u)]=lzm[ls(u)]*lzm[u]%mod;lzm[rs(u)]=lzm[rs(u)]*lzm[u]%mod;
mul[ls(u)]=mul[ls(u)] * qpow(lzm[u],mid-l+1) % mod;
mul[rs(u)]=mul[rs(u)] * qpow(lzm[u],r-mid) % mod;
}
lzm[u]=1;
lzt[u].reset();
}
void update(int u,int l,int r,int fl,int fr,int MUL){
if(fl<=l && fr>=r){
mul[u]=mul[u]*qpow(MUL,r-l+1)%mod;
lzm[u]=lzm[u]*MUL%mod;
tag[u]|=y_num[MUL];
lzt[u]|=y_num[MUL];
return;
}
push_down(u,l,r);
int mid=(l+r)>>1;
if(fl<=mid){
update(ls(u),l,mid,fl,fr,MUL);
}
if(fr>mid){
update(rs(u),mid+1,r,fl,fr,MUL);
}
push_up(u);
}
int find(int u,int l,int r,int fl,int fr){
if(fl<=l && r<=fr){
return mul[u];
}
push_down(u,l,r);
int mid=(l+r)>>1;
int ans=1;
if(fl<=mid){
ans=find(ls(u),l,mid,fl,fr)*ans%mod;
}
if(fr>mid){
ans=find(rs(u),mid+1,r,fl,fr)*ans%mod;
}
return ans;
}
bitset<70> find_pri(int u,int l,int r,int fl,int fr){
if(fl<=l && r<=fr){
return tag[u];
}
push_down(u,l,r);
int mid=(l+r)>>1;
bitset<70> ans;
ans.reset();
if(fl<=mid){
ans=find_pri(ls(u),l,mid,fl,fr)|ans;
}
if(fr>mid){
ans=find_pri(rs(u),mid+1,r,fl,fr)|ans;
}
return ans;
}
signed main(){
// ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
init();
// cin >> n >> q;
scanf("%lld%lld",&n,&q);
// read(n);
// read(q);
// for(int x=1;x<=32;x++){
// cout << prim[x] << ' ';
// }
// cout << '\n';
for(int x=1;x<=n;x++){
scanf("%lld",&a[x]);
// read(a[x]);
}
build(1,1,n);
for(int x=1;x<=q;x++){
int op,l,r,X;
scanf("%lld%lld%lld",&op,&l,&r);
// read(op);read(l);read(r);
if(op==1){
scanf("%lld",&X);
// read(X);
update(1,1,n,l,r,X);
}else{
bitset<70> ans=find_pri(1,1,n,l,r);
int Ans=find(1,1,n,l,r);
// cout << ans << '\n';
for(int y=1;y<=62;y++){
if(ans[y]==1){
Ans=Ans*(prim[y]-1)%mod*in_e[y]%mod;
}
}
wt(Ans);
printf("\n");
}
}
return 0;
}