there is a longer way ahead, After all.

CF1797E

难点在于问题的转化。

根据结论,我们可知,操作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;
}
posted @ 2026-11-04 10:16  WinterXorSnow  阅读(2)  评论(0)    收藏  举报