there is a longer way ahead, After all.
难点在于问题的转化。
根据结论,我们可知,操作1对于每一个位置,只有 \(\log V\) 次是有效的。那么问题在于操作2我们不好处理。
考虑对于每一个位置,操作的结果会形成一条条路径,则操作2相当于在问哪个点可以在所有路径上出现。因此注意到我们可以建树,连边为 \(phi(i) \to i\)。则操作2相当于询问区间 \(LCA\),根据结论,我们只需要知道区间内 \(dfn\) 最小值和最大值即可得到结果。
使用线段树随便维护一下就好了。
时间复杂度双老哥。
Code
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false);cin.tie(0),cout.tie(0)
#define File(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout)
#define LL long long
#define fi first
#define se second
const int N = 1e5 + 10, V = 5e6 + 10;
int phi[V],vis[V],pri[V],len;
int n,Q,a[N];
vector<int> G[V];
int dfn[V],idfn[V],L[V],R[V],dep[V],tot;
void dfs(int u){
dfn[u] = ++tot;
idfn[tot] = u;
L[u] = tot;
for(int v : G[u]){
dep[v] = dep[u] + 1;
dfs(v);
}
R[u] = tot;
}
void init(int n){
phi[1] = 1;
for(int i=2;i<=n;i++){
if(vis[i] == 0){
pri[++len] = i;
phi[i] = i-1;
}
for(int j=1;j<=len;j++){
if(i * pri[j] > n) break;
vis[i*pri[j]] = 1;
if(i % pri[j] == 0){
phi[i*pri[j]] = phi[i] * pri[j];
break;
}
phi[i*pri[j]] = phi[i] * phi[pri[j]];
}
G[phi[i]].push_back(i);
}
dfs(1);
return ;
}
struct node{
int val;
int cnt;
int mn,mx;
int x;
};
struct segment_tree{
node tre[N<<2];
void pushup(int p){
tre[p].val = tre[p<<1].val + tre[p<<1|1].val;
tre[p].cnt = tre[p<<1].cnt + tre[p<<1|1].cnt;
tre[p].mx = (tre[p<<1].mx > tre[p<<1|1].mx) ? tre[p<<1].mx : tre[p<<1|1].mx;
tre[p].mn = (tre[p<<1].mn < tre[p<<1|1].mn) ? tre[p<<1].mn : tre[p<<1|1].mn;
}
node merge(node x,node y){
node res;
res.val = x.val + y.val;
res.cnt = x.cnt + y.cnt;
res.mx = max(x.mx,y.mx);
res.mn = min(x.mn,y.mn);
return res;
}
void update(int L,int R,int pl,int pr,int p){
if(tre[p].cnt == 0) return ;
if(pr < L || pl > R || pl > pr) return ;
if(pl == pr){
tre[p].x = phi[tre[p].x];
tre[p].mn = tre[p].mx = dfn[tre[p].x];
if(tre[p].x == 1) tre[p].cnt = 0;
else tre[p].cnt = 1;
tre[p].val = dep[tre[p].x];
return ;
}
int mid = pl + pr >> 1;
update(L,R,pl,mid,p<<1);
update(L,R,mid+1,pr,p<<1|1);
pushup(p);
}
node query(int L,int R,int pl,int pr,int p){
if(L <= pl && pr <= R) return tre[p];
int mid = pl + pr >> 1;
if(L <= mid && mid < R) return merge(query(L,R,pl,mid,p<<1),query(L,R,mid+1,pr,p<<1|1));
if(L <= mid) return query(L,R,pl,mid,p<<1);
return query(L,R,mid+1,pr,p<<1|1);
}
void build(int pl,int pr,int p){
if(pl == pr){
tre[p].x = a[pl];
tre[p].mn = tre[p].mx = dfn[tre[p].x];
if(tre[p].x == 1) tre[p].cnt = 0;
else tre[p].cnt = 1;
tre[p].val = dep[tre[p].x];
return ;
}
int mid = pl + pr >> 1;
build(pl,mid,p<<1);
build(mid+1,pr,p<<1|1);
pushup(p);
}
}sgt;
int LCA(int x,int y){
if(dep[x] > dep[y]) swap(x,y);
while(!(L[x] <= L[y] && R[y] <= R[x])) x = phi[x];
return x;
}
int main(){
IOS;
init(V-10);
cin >> n >> Q;
for(int i=1;i<=n;i++)
cin >> a[i];
sgt.build(1,n,1);
while(Q -- ){
int op; cin >> op;
if(op == 1){
int l,r; cin >> l >> r;
sgt.update(l,r,1,n,1);
}
else{
int l,r; cin >> l >> r;
node res = sgt.query(l,r,1,n,1);
int lca = LCA(idfn[res.mn],idfn[res.mx]);
cout << res.val - 1ll * dep[lca] * (r - l + 1) << "\n";
}
}
return 0;
}

浙公网安备 33010602011771号