题目链接:
题意:
给定一个长度为n的数组x,有m次操作,操作有两种,操作0是让区间[l,r]内的数加上x,操作1是输出[l,r]区间内的(x的欧拉函数值)的和。
输入第一行给n,m,第二行给出,接下来m行是m次操作。
思路:
首先预处理出1~100的所有素数和对应的欧拉函数值,用bitset维护100内的所有质数(tb[i][j]表示数字i的因子包不包含prime[j]),用cnt[i][j]数组维护数字i含有prime[j]的个数;
线段树维护的值有两个,一个是区间欧拉函数值的和,另一个是区间包含的公共素因子(代码中用bitset类型的tag,维护也很简单,共同素因子就直接左右子区间&就可以了)。
在进行区间乘的修改时,我们把数w分解成若干个质因子去进行修改,之前cnt数组中已经预处理出了1~100内所有数对应prime[j]的个数,将{j,cnt[w][j]}作为参数传给modify函数。
线段树在进行修改时,如果区间包含公共素因子prime[j],即tr[u].tag[j]==1,相当于区间内所有数的欧拉函数值都乘,也就是整个区间欧拉和也乘;
如果没有被包含,那么我们就交给子树来处理;
如果处理到叶子了还没被包含,那么直接暴力单点修改,叶子节点的欧拉函数值变成乘,sum是原欧拉函数值,p是prime[j],然后将tr[u].tag[j]置1。
样例:
输入
5 5
1 2 3 4 5
1 1 5
0 1 3 2
1 1 5
0 2 5 6
1 1 5
输出
10
11
37
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int MOD=998244353;
const int N=1e5+10;
const int M=101;
int euler[M],prime[N];
bool isprime[M];
int n,m;
int cnt[M][30];
bitset<30> tb[M];
int x[N];
typedef struct Node{
int l,r;
ll sum,lazy;
bitset<30> tag;
}Node;
Node tr[N*4];
void init(){
//求素数和欧拉函数
isprime[1]=1;
for(int i=2;i<M;i++){
if(!isprime[i]){
prime[++prime[0]] = i;
euler[i]=i-1;
}
for(int j=1;j<=prime[0]&&(ll)prime[j]*i<M;j++){
int tp=prime[j]*i;
isprime[tp]=1;
if(i%prime[j]==0){
euler[tp]=euler[i]*prime[j];
break;
}
euler[tp]=euler[i]*(prime[j]-1);
}
}
euler[1] = 1;
//预处理出1~100含每个素数的个数
int x;
for(int i=1;i<M;i++){
x=i;
for(int j=1;j<=prime[0];j++){
if(x<prime[j])
break;
while(x%prime[j]==0){
tb[i][j]=1;
cnt[i][j]++;
x/=prime[j];
}
}
}
}
ll ksm(ll a,ll b){
ll res=1;
while(b){
if(b%2)
res=(res*a)%MOD;
a=a*a%MOD;
b=b/2;
}
return res;
}
void pushup(int u){
tr[u].sum=(tr[u<<1].sum+tr[u<<1|1].sum)%MOD;
tr[u].tag=tr[u<<1].tag&tr[u<<1|1].tag;
}
void build(int u,int l,int r){
tr[u].l=l,tr[u].r=r,tr[u].lazy=1;
if(l==r){
tr[u].sum=euler[x[l]];
tr[u].tag=tb[x[l]];
return ;
}
int mid=(l+r)>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
pushup(u);
}
//p.first是质数标号,p.second是质数个数
void tag_union(int fa,int u){
ll k=tr[fa].lazy;
tr[u].lazy=(tr[u].lazy*k)%MOD;
tr[u].sum=(tr[u].sum*k)%MOD;
}
void pushdown(int u){
if(tr[u].lazy!=1){
tag_union(u,u<<1);
tag_union(u,u<<1|1);
tr[u].lazy=1;
}
}
void modify(int u,int l,int r,PII p){
if(tr[u].l>=l&&tr[u].r<=r&&tr[u].tag[p.first]){
ll k=ksm(prime[p.first],p.second);
tr[u].lazy=(tr[u].lazy*k)%MOD;
tr[u].sum=(tr[u].sum*k)%MOD;
return ;
}
if(tr[u].l==tr[u].r){//已经到叶子节点了
ll k=ksm(prime[p.first],p.second-1);
tr[u].sum=(tr[u].sum*k*(prime[p.first]-1))%MOD;
tr[u].tag[p.first]=1;
return ;
}
pushdown(u);
int mid=(tr[u].l+tr[u].r)>>1;
if(l<=mid)
modify(u<<1,l,r,p);
if(r>mid)
modify(u<<1|1,l,r,p);
pushup(u);
}
ll query(int u,int l,int r){
if(tr[u].l>=l&&tr[u].r<=r){
return tr[u].sum;
}
pushdown(u);
ll res=0;
int mid=(tr[u].l+tr[u].r)>>1;
if(l<=mid)
res=query(u<<1,l,r);
if(r>mid)
res=(res+query(u<<1|1,l,r))%MOD;
return res;
}
int main(){
init();
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&x[i]);
build(1,1,n);
int op,l,r,w;
while(m--){
scanf("%d",&op);
if(op==0){
scanf("%d%d%d",&l,&r,&w);
for(int j=1;j<=prime[0];j++){
if(prime[j]>w)
break;
if(cnt[w][j]){
modify(1,l,r,{j,cnt[w][j]});
}
}
}
else{
scanf("%d%d",&l,&r);
printf("%lld\n",query(1,l,r));
}
}
}