势能线段树 + 欧拉函数

L题
题解
在这里插入图片描述

Sample Input

5 5
1 2 3 4 5
1 1 5
0 1 3 2
1 1 5
0 2 5 6
1 1 5

Sample Output

10
11
37

题意:
操作一:区间 [ l , r ] [l,r] [l,r] w w w,操作二:查询 ∑ i = l i = r φ ( a [ i ] ) \sum_{i=l}^{i=r}\varphi(a[i]) i=li=rφ(a[i])
φ ( x ) \varphi(x) φ(x) x x x 的欧拉函数
思路:
考虑到区间查询是求每个数欧拉函数的和,因此我们要去维护区间欧拉函数的和

欧拉函数特殊的性质:
m m m 为质数

  1. 如果 n   %   m ! = 0 n \ \% \ m!=0 n % m!=0 互质,那么 φ ( n ∗ m ) = φ ( n ) ∗ φ ( m ) = φ ( n ) ∗ ( m − 1 ) \varphi(n*m)=\varphi(n)*\varphi(m)=\varphi(n)*(m-1) φ(nm)=φ(n)φ(m)=φ(n)(m1)
  2. 如果 n   %   m = 0 n \ \% \ m=0 n % m=0,那么 φ ( n ∗ m ) = φ ( n ) ∗ m \varphi(n*m)=\varphi(n)*m φ(nm)=φ(n)m

根据唯一分解定理: n = p 1 q 1 p 2 q 2 p 3 q 3 . . . p n q n n=p_1^{q_1}p_2^{q_2}p_3^{q_3}...p_n^{q_n} n=p1q1p2q2p3q3...pnqn
可以得到 φ ( x ) = φ ( p 1 q 1 ) φ ( p 2 q 2 ) φ ( p 3 q 3 ) . . . φ ( p n q n ) \varphi(x)=\varphi(p_1^{q_1})\varphi(p_2^{q_2})\varphi(p_3^{q_3})...\varphi(p_n^{q_n}) φ(x)=φ(p1q1)φ(p2q2)φ(p3q3)...φ(pnqn)
由于我们每次乘的 w w w 都在 1 − 100 1-100 1100 之内,我们可以直接预处理出每个数含有的质因子的情况
然后每个数乘以 p 1 q 1 p_1^{q_1} p1q1 即可。
但是我们还要讨论每个数乘 p 1 q 1 p_1^{q_1} p1q1 ,该如何维护每个数的欧拉函数值

  1. 如果区间内每个数都不含质因子 p 1 p_1 p1,那么每个数乘 p 1 q 1 p_1^{q_1} p1q1 次方,相当于每个数的欧拉函数值乘以 φ ( p 1 q 1 ) \varphi(p_1^{q_1}) φ(p1q1)
  2. 如果区间内每个数都含有质因子 p 1 p_1 p1,考虑到每个数的 p 1 p_1 p1质因子次方参差不齐,我们先一个一个乘。每个数乘 p 1 p_1 p1 相当于 每个数的欧拉函数值也乘 p 1 p_1 p1。显然,乘 q 1 q_1 q1 p 1 p_1 p1 与 直接乘以 p 1 q 1 p_1^{q_1} p1q1 是等价的。
  3. 都不符合就暴力递归

每个节点开一个长度为 25 25 25 b i t s e t bitset bitset 表示区间内是否所有数都含有这个质因子。
当整个区间的数都含有质因子 p p p 时,可以直接打上一个乘法标记,没有则向下递归。
由于只有 25 25 25 个质数,所以每个节点至多被暴力修改 25 25 25 次。

code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e5 + 9;
const int maxm = 109;
const int mod = 998244353;
bitset <maxm> vis;// 标记是否为质因子 
int eul[maxm], p[maxm], id[maxm];// 质因子 i 为第 id[i]+1 个质因子(从2开始 
vector <pair<int,int> > val[maxm];// v[i]存 i 的所以质因子的最高次方及质因子的 id 
bitset<26> G[maxm];// G[i][j] 代表 i 能否被 j 整除 

void init()
{
	eul[1] = 1;
	for(int i = 2; i <= 100; ++i)
	{
		if(!vis[i]) p[++p[0]] = i, eul[i] = i - 1, val[i] = {{i, id[i] = p[0] - 1}}, G[i][p[0] - 1] = 1;
		for(int j = 1, x; j <= p[0] && (x = i * p[j]) <= 100; ++j)
		{
			vis[x].flip(), val[x] = val[i], G[x] = G[i];
			if(i % p[j]) eul[x] = eul[i] * (p[j] - 1), val[x].emplace_back(p[j], id[p[j]]), G[x][id[p[j]]] = 1;
			else 
			{
				eul[x] = eul[i] * p[j];
				for(auto &w : val[x]) if(w.second == id[p[j]])
					w.first *= p[j];
				break;
			}
		}
	}
}


int n, m;
int a[maxn];
struct node
{
	int mul, sum;
	bitset <26> f;
}tr[maxn << 2];
#define lc (p << 1)
#define rc (p << 1 | 1)
#define endl '\n'
void pushup(int p)
{
	tr[p].sum = (tr[lc].sum + tr[rc].sum) % mod;
	tr[p].f = tr[lc].f & tr[rc].f;
}
void pushdown(int p){
	if(tr[p].mul > 1)
	{
		tr[lc].sum = 1ll * tr[lc].sum * tr[p].mul % mod;
		tr[lc].mul = 1ll * tr[lc].mul * tr[p].mul % mod;
		tr[rc].sum = 1ll * tr[rc].sum * tr[p].mul % mod;
		tr[rc].mul = 1ll * tr[rc].mul * tr[p].mul % mod;	
		tr[p].mul = 1;
	}
}
void build(int p = 1, int cl = 1, int cr = n)
{
	tr[p].mul = 1;
	if(cl == cr){
		tr[p].f = G[a[cl]], tr[p].sum = eul[a[cl]];return;
	}
	int mid = (cl + cr) >> 1;
	build(lc, cl, mid); 
	build(rc, mid + 1, cr);
	pushup(p);
}
void update(int l, int r, int x, int i, int p = 1, int cl = 1, int cr = n)
{
	if(cl >= l && cr <= r && tr[p].f[i]){
		tr[p].mul = 1ll * tr[p].mul * x % mod;
		tr[p].sum = 1ll * tr[p].sum * x % mod;
		return;
	}
	if(cl == cr){
		tr[p].f[i] = 1, tr[p].sum = 1ll * tr[p].sum * eul[x] % mod; return;
	}
	int mid = (cl + cr) >> 1;
	pushdown(p);
	if(mid >= l) update(l, r, x, i, lc, cl, mid);
	if(mid < r) update(l, r, x, i, rc, mid + 1, cr);
	pushup(p);
}
int query(int l, int r, int p = 1, int cl = 1, int cr = n)
{
	if(cl >= l && cr <= r) return tr[p].sum;
	int mid = (cl + cr) >> 1, ans = 0;
	pushdown(p);
	if(mid >= l) ans = query(l, r, lc, cl, mid) % mod;
	if(mid < r) ans = (ans + query(l, r, rc, mid + 1, cr)) % mod;
	return ans;
}
void work()
{
	cin >> n >> m;
	for(int i = 1; i <= n; ++i) cin >> a[i];
	build();
	while(m--)
	{
		int op, l, r;
		cin >> op >> l >> r;
		if(!op){
			int w;cin >> w;
			for(auto x : val[w])
				update(l, r, x.first, x.second);
		}
		else cout << query(l, r) << endl;
	}
}
int main()
{
	ios::sync_with_stdio(0);
	init();
	work();
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值