-
題意:一個長度為\(n\)的序列\(x\),\(m\)次操作,有兩種,一種是對區間\([l,r]\)的數乘上\(w\),一種是詢問\([l,r]\)的每個數的歐拉函數之和。
-
題解:首先看數據范圍,\(x[i]\)和\(w\)的值都很小,最大才\(100\),根據歐拉函數公式:\(\phi [N]=N*(1-\frac{1}{p_1})*...*(1-\frac{1}{p_n})\),\(p_i\)表示\(N\)質因數分解后的質數,那么有如下的性質:
對於一個質數P,\(\phi [N*P]=\phi [N]*P\ \ if(N|P)\)和\(\phi [N*P]=\phi [N]*(P-1)\ \ if(N!|P)\)
那么對於每個\(w\),先質因數分解,然后線段樹維護每個質因數,並開25個\(tag\)進行標記,如果當前區間全部包含某個質因數,就可以直接算。
-
代碼:
#include <iostream> #include <vector> #include <set> #include <algorithm> #include <unordered_map> using namespace std; #define ll long long #define pb push_back const int N=1e5+10; const int mod=998244353; int n,m; ll x[N]; ll res[200]; //歐拉函數值 int cntp[200][200]; //記錄每個i的質數j的出現次數 bool vis[200]; int prime[100],cnt; void get_prime(int n){ for(int i=2;i<=n;++i){ if(!vis[i]) prime[cnt++]=i; for(int j=0;j<cnt && prime[j]<=n/i;++j){ vis[i*prime[j]]=true; if(i%prime[j]==0) break; } } } struct Node{ int l,r; ll val; ll mul; bool tag[26]; }tr[N<<4]; void push_up(int u){ for(int i=0;i<25;++i){ //如果兩個兒子都有這個質因數,父親才有 if(tr[u<<1].tag[i] && tr[u<<1|1].tag[i]){ tr[u].tag[i]=true; } } tr[u].val=(tr[u<<1].val+tr[u<<1|1].val)%mod; } void push_down(int u){ tr[u<<1].val=(tr[u<<1].val*tr[u].mul)%mod; tr[u<<1|1].val=(tr[u<<1|1].val*tr[u].mul)%mod; tr[u<<1].mul=(tr[u<<1].mul*tr[u].mul)%mod; tr[u<<1|1].mul=(tr[u<<1|1].mul*tr[u].mul)%mod; tr[u].mul=1; } void build(int u,int l,int r){ if(l==r){ tr[u]={l,r,res[x[l]],1}; for(int i=0;i<25;++i){ if(cntp[x[l]][i]) tr[u].tag[i]=true; } return; } tr[u]={l,r,1,1}; int mid=(tr[u].l+tr[u].r)>>1; build(u<<1,l,mid); build(u<<1|1,mid+1,r); push_up(u); } void update(int u,int l,int r,int id,int cnt){ if(tr[u].l>=l && tr[u].r<=r && tr[u].tag[id]){ //質因數出現過 for(int i=1;i<=cnt;++i){ tr[u].val=(tr[u].val*prime[id])%mod; tr[u].mul=(tr[u].mul*prime[id])%mod; } return; } if(tr[u].l==tr[u].r){ //質因數沒有出現過 tr[u].val=(tr[u].val*(prime[id]-1))%mod; //先算一次 for(int i=1;i<cnt;++i){ tr[u].val=(tr[u].val*prime[id])%mod; } tr[u].tag[id]=true; return; } if(tr[u].mul!=1) push_down(u); int mid=(tr[u].l+tr[u].r)>>1; if(l<=mid) update(u<<1,l,r,id,cnt); if(r>mid) update(u<<1|1,l,r,id,cnt); push_up(u); } ll query(int u,int l,int r){ if(tr[u].l>=l && tr[u].r<=r){ return tr[u].val; } if(tr[u].mul!=1) push_down(u); int mid=(tr[u].l+tr[u].r)>>1; ll sum=0; if(l<=mid) sum=(sum+query(u<<1,l,r))%mod; if(r>mid) sum=(sum+query(u<<1|1,l,r))%mod; return sum; } int main(){ get_prime(110); scanf("%d %d",&n,&m); for(int i=1;i<=n;++i){ scanf("%lld",&x[i]); } for(int i=1;i<=100;++i){ int tmp=i; res[i]=i; for(int j=0;j<25;++j){ if(tmp%prime[j]==0){ res[i]=res[i]/prime[j]*(prime[j]-1); while(tmp%prime[j]==0) tmp/=prime[j],cntp[i][j]++; } } } build(1,1,n); for(int i=1;i<=m;++i){ int op; scanf("%d",&op); if(op==1){ int l,r; scanf("%d %d",&l,&r); printf("%lld\n",query(1,l,r)); } else{ int l,r,x; scanf("%d %d %d",&l,&r,&x); for(int i=0;i<25;++i){ if(cntp[x][i]) update(1,l,r,i,cntp[x][i]); } } } return 0; }