分析:
根据欧拉函数的那个性质
if(p是质数)
{
if(i % p == 0) f[i * p] = f[i] * p;
else f[i * p] = f[i] * (p - 1);
}
每次区间乘的那个数小于等于100,所以我们可以考虑把100以内的数质因数分解,区间乘100相当于区间乘两个2和两个5,但是根据那个性质,又分为了两种情况,到底需要乘p还是p - 1?对于每个区间我们可以维护一个bitset<30> tg,表示这个区间的数是否所有的都是第i个素数的倍数,显然,经过几次操作之后,大部分的区间的每个tg值都会变为1,此时都是乘p,比较像区间加/减lowbit的那个题。
另外,用这个bitset,实际可以用bool数组的,但实测用bitset约250ms,bool数组490ms,另外bitset可以一下子赋值,不需要跑for循环一个一个的赋值
AC代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 998244353;
bitset<30> st[105];//st[i][j]表示数字i能否被第j个质数整除
struct node
{
bitset<30> tg;
ll x, lazy;
int l, r;
} tree[400050];
int n, m;
int ar[100050];
int pri[30];//100以内的质数,一共25个
int cnt[105][30];//cnt[i][j]表示数字i质因数分解后需要cnt[i][j]个第j个质数
int f[105];//欧拉函数
void dio()//预处理数组pri和cnt
{
int tot = 0;
for(int i = 2; i <= 100; ++i)
{
bool flag = 0;
for(int j = 2; j <= sqrt(i); ++j)
{
if(i % j == 0)
{
flag = 1;
break;
}
}
if(!flag) pri[++tot] = i;
}
for(int i = 1; i <= 100; ++i)
{
for(int j = 1; j <= 25; ++j)
{
int tmp = i;
while(tmp % pri[j] == 0)
{
++cnt[i][j];
tmp /= pri[j];
}
st[i][j] = cnt[i][j];
}
}
}
int phi(int n)//计算欧拉函数
{
int ans = n;
for(int i = 2; i * i <= n; ++i)
{
if(n % i == 0)
{
ans = ans / i * (i - 1);
while(n % i == 0) n /= i;
}
}
if(n > 1) ans = ans / n * (n - 1);
return ans;
}
void pushup(int p)
{
tree[p].x = (tree[p<<1].x + tree[p<<1|1].x) % mod;
tree[p].tg = (tree[p<<1].tg & tree[p<<1|1].tg);
}
void pushdown(int p)
{
tree[p<<1].lazy = (tree[p<<1].lazy * tree[p].lazy) % mod;
tree[p<<1|1].lazy = (tree[p<<1|1].lazy * tree[p].lazy) % mod;
tree[p<<1].x = (tree[p<<1].x * tree[p].lazy) % mod;
tree[p<<1|1].x = (tree[p<<1|1].x * tree[p].lazy) % mod;
tree[p].lazy = 1;
}
void build(int p, int l, int r)
{
tree[p].l = l;
tree[p].r = r;
tree[p].lazy = 1;
if(tree[p].l == tree[p].r)
{
tree[p].x = 1ll * f[ar[l]];
tree[p].lazy = 1;
tree[p].tg = st[ar[l]];
return ;
}
int mid = (l + r) >> 1;
build(p<<1, l, mid);
build(p<<1|1, mid + 1, r);
pushup(p);
}
ll query(int p, int a, int b)
{
if(a <= tree[p].l && tree[p].r <= b) return tree[p].x;
if(tree[p].lazy != 1) pushdown(p);
int mid = (tree[p].l + tree[p].r) >> 1;
if(mid >= b) return query(p<<1, a, b);
else if(mid < a) return query(p<<1|1, a, b);
else return (query(p<<1, a, b) + query(p<<1|1, a, b)) % mod;
}
void updata(int p, int a, int b, int x, int y)
{
if(a <= tree[p].l && tree[p].r <= b && tree[p].tg[x])
{
for(int i = 1; i <= y; ++i)
{
tree[p].x = (tree[p].x * pri[x]) % mod;
tree[p].lazy = (tree[p].lazy * pri[x]) % mod;
}
return ;
}
if(tree[p].l == tree[p].r)
{
tree[p].x = (tree[p].x * (pri[x] - 1)) % mod;
tree[p].tg[x] = 1;
for(int i = 1; i <= y - 1; ++i) tree[p].x = (tree[p].x * pri[x]) % mod;
return ;
}
if(tree[p].lazy != 1) pushdown(p);
int mid = (tree[p].l + tree[p].r) >> 1;
if(a <= mid) updata(p<<1, a, b, x, y);
if(mid < b) updata(p<<1|1, a, b, x, y);
pushup(p);
}
int main()
{
dio();
f[1] = 1;
for(int i = 2; i <= 100; ++i) f[i] = phi(i);
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i) scanf("%d", &ar[i]);
build(1, 1, n);
int y, a, b, c;
while(m--)
{
scanf("%d", &y);
if(y == 1)
{
scanf("%d%d", &a, &b);
printf("%lld\n", query(1, a, b) % mod);
}
else
{
scanf("%d%d%d", &a, &b, &c);
for(int i = 1; i <= 25; ++i)
{
if(cnt[c][i]) updata(1, a, b, i, cnt[c][i]);
}
}
}
return 0;
}