• The 2021 ICPC Asia Regionals Online Contest (II) L Euler Function


    思路来源:Zed222

    如果一个区间里的数都有这个质数,那么我们就直接利用性质\(\phi(n * p) = \phi(n) * p\),如果没有这个区间中有没有这个质数的,那么就退化到了单点修改,当时比赛的时候,队伍感觉就没有了头绪,而今天补题发现确实是单点修改,并且代码跑的飞快,具体的证明就不深究了,还有当时并不会存以一个区间里存在有哪些质数(维护区间有哪些质数,\(pushup\)的时候我们用与操作),然后通过做了CF. Please, another Queries on Array?这道题学到\(300\)以内只有六十一个质数,所以可以用状压的方式存,而本题\(100\)以内的质数有二十多个,也采用状压的方式。

    进入正题:

    • 当一个区间里的数都有这个质数的时候,直接利用性质\(\phi(n \times p) = \phi(n) \times p\),然后\(\sum\limits_{i = l}^{r}\phi(x_i \times p^k) = p^k \times \sum\limits_{i = l}^{r}\phi(x_i)\)

    • 否则进行单点修改,当tr[u].l == tr[u].r时,进行单点修改。
      为了单点修改方便,我们利用算术基本定理化简一下式子:\(\phi(n) = n \times \prod\limits \cfrac{(p_i - 1)}{p_i} = \prod\limits (p_i - 1) \times p_i^{a_{i - 1}}\)

    #include <bits/stdc++.h>
    
    using namespace std;
    
    typedef long long LL;
    
    const int N = 1E5 + 10, Mod = 998244353, M = 105;
    int euler[M], cntp, primes[M], state[M];
    int a[N];
    int cnt[M][30];
    bitset<30> st[M]; 
    
    struct SegMentTree {
    	int l, r;
    	LL sum;
    	LL laz;
    	bitset<30> has;
    }tr[N * 4];
    
    void get_euler(int n) {
    	euler[1] = 1;
    	for (int i = 2; i <= n; i++) {
    		if (!state[i]) primes[cntp++] = i, euler[i] = i - 1;
    		for (int j = 0; primes[j] <= n / i; j++) {
    			state[i * primes[j]] = true;
    			if (i % primes[j] == 0) {
    				euler[i * primes[j]] = euler[i] * primes[j];
    				break;
    			}
    			euler[i * primes[j]] = euler[i] * (primes[j] - 1);
    		}
    	}
    }
    
    LL qmi(LL a, LL b, LL Mod) {
    	LL res = 1ll;
    	while (b) {
    		if (b & 1) res = res * a % Mod;
    		b >>= 1;
    		a = a * a % Mod;
    	}
    	return res % Mod;
    }
    
    void pushup(int u) {
    	tr[u].sum = (tr[u << 1].sum + tr[u << 1 | 1].sum) % Mod;
    	tr[u].has = tr[u << 1].has & tr[u << 1 | 1].has;
    }
    
    void pushdown(int u) {
    	auto &root = tr[u], &left = tr[u << 1], &right = tr[u << 1 | 1];
    	if (root.laz == 1) return;
    	left.sum = left.sum * root.laz % Mod;
    	right.sum = right.sum * root.laz % Mod;
    	left.laz = left.laz * root.laz % Mod;
    	right.laz = right.laz * root.laz % Mod;
    	root.laz = 1;
    }
    
    void build(int u, int l, int r) {
    	if (l == r) {
    		tr[u] = {l, r, euler[a[l]], 1ll, st[a[l]]};
    		return;
    	}
    	tr[u] = {l, r}, tr[u].laz = 1ll;
    	int mid = l + r >> 1;
    	build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
    	pushup(u);
    }
    
    void modify(int u, int l, int r, int k, int ct) {
    	int p = primes[k];
    	if (tr[u].l >= l && tr[u].r <= r && tr[u].has[k]) { //当前区间有这个质因子
    		tr[u].sum = tr[u].sum * qmi(p, ct, Mod) % Mod;
    		tr[u].laz = tr[u].laz * qmi(p, ct, Mod) % Mod;
    	} else if (tr[u].l == tr[u].r) { //直至单点修改
    		tr[u].has[k] = 1;
    		tr[u].sum = tr[u].sum * (p - 1) % Mod;
    		tr[u].sum = tr[u].sum * qmi(p, ct - 1, Mod) % Mod;
    	} else {
    		pushdown(u);
    		int mid = tr[u].l + tr[u].r >> 1;
    		if (l <= mid) modify(u << 1, l, r, k, ct);
    		if (r > mid) modify(u << 1 | 1, l, r, k, ct);
    		pushup(u);
    	}
    }
    
    LL query(int u, int l, int r) {
    	if (tr[u].l >= l && tr[u].r <= r) {
    		return tr[u].sum;
    	} else {
    		pushdown(u);
    		int mid = tr[u].l + tr[u].r >> 1;
    		LL res = 0;
    		if (l <= mid) res = (res + query(u << 1, l, r)) % Mod;
    		if (r > mid) res = (res + query(u << 1 | 1, l, r)) % Mod;
    		return res;
    	}
    }
    
    int main() {
    	get_euler(100);
    	for (int i = 1; i <= 100; i++) {
    		for (int j = 0; j < cntp; j++) {
    			if (i % primes[j] == 0) {
    				int s = 0, temp = i;
    				while (temp % primes[j] == 0) s++, temp /= primes[j];
    				cnt[i][j] = s;
    				st[i][j] = (cnt[i][j] > 0);
    			}
    		}
    	}
    	int n, m;
    	scanf("%d%d", &n, &m);
    	for (int i = 1; i <= n; i++) {
    		scanf("%d", &a[i]);
    	}
    	
    	build (1, 1, n);
    	
    	while (m--) {
    		int op, l, r;
    		scanf("%d%d%d", &op, &l, &r);
    		if (op == 0) {
    			int w;
    			scanf("%d", &w);
    			for (int i = 0; i < cntp; i++) {
    				if (cnt[w][i]) {
    					modify(1, l, r, i, cnt[w][i]);
    				}
    			}
    		} else {
    			printf("%lld\n", query(1, l, r));
    		}
    	}
    	
        return 0;
    }
    
  • 相关阅读:
    了解自我
    IT技能栈
    客户端的工作不仅仅只是看起来那么简单
    .NET 基础 一步步 一幕幕[XML基础操作]
    .NET 基础 一步步 一幕幕[Winform应用程序]
    .NET 基础 一步步 一幕幕[面向对象之堆、栈、引用类型、值类型]
    .NET 基础 一步步 一幕幕[面向对象之new、this关键字]
    .NET 基础 一步步 一幕幕[面向对象之静态、非静态]
    .NET 基础 一步步 一幕幕[面向对象之方法、方法的重载、方法的重写、方法的递归]
    .NET 基础 一步步 一幕幕[面向对象之构造函数、析构函数]
  • 原文地址:https://www.cnblogs.com/ZhengLijie/p/15546388.html
Copyright © 2020-2023  润新知