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

思路来源:Zed222

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

进入正题:

  • 当一个区间里的数都有这个质数的时候,直接利用性质ϕ(n×p)=ϕ(n)×p,然后i=lrϕ(xi×pk)=pk×i=lrϕ(xi)

  • 否则进行单点修改,当tr[u].l == tr[u].r时,进行单点修改。
    为了单点修改方便,我们利用算术基本定理化简一下式子:ϕ(n)=n×(pi1)pi=(pi1)×piai1

#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;
}
posted @   Xxaj5  阅读(81)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端
点击右上角即可分享
微信分享提示