当前位置:   article > 正文

【C++】B3716分解质因子3 题解_素数筛法_欧拉筛+埃氏筛_算法竞赛

【C++】B3716分解质因子3 题解_素数筛法_欧拉筛+埃氏筛_算法竞赛

B3716 分解质因子 3 题解

Link:Luogu - B3716

题目描述

给定一个正整数 n n n,设 n = p 1 × p 2 × … p k n = p_1 \times p_2 \times \dots p_k n=p1×p2×pk,其中 p i p_i pi 均为质数,对 1 ≤ i < k 1 \leq i < k 1i<k p i ≤ p i + 1 p_i \leq p_{i + 1} pipi+1

可以证明,序列 p i p_i pi 是唯一的。

对每个给定的 n n n,请你求出 p 1 , p 2 , … p k p_1, p_2, \dots p_k p1,p2,pk

为了避免输出过大,请你输出 p 1 , p 2 , … p k p_1, p_2, \dots p_k p1,p2,pk按位异或和

输入格式

本题单测试点内有多组测试数据

第一行是一个整数,表示测试数据组数 T T T

接下来 T T T 行,每行一个整数,表示一组数据的 n n n

输出格式

对每组测试数据,输出一行一个整数,表示它所有质因子的按位异或和。

样例 #1

样例输入 #1

2
3
9
  • 1
  • 2
  • 3

样例输出 #1

3
0
  • 1
  • 2

提示

数据规模与约定

对于全部的测试点,保证 1 ≤ T ≤ 1 0 6 1 \leq T \leq 10^6 1T106 2 ≤ n ≤ 1 0 8 2 \leq n \leq 10^8 2n108

提示

请注意大量数据读入输出对程序效率造成的影响,选择合适的 IO 方式,避免超时。


解题思路

这道题的思路非常巧妙,值得记录。是典型的素数筛法变式题

首先看到这道题数据范围这么大,又是素数相关,不难联想到要利用素数筛预处理。

回顾朴素的素数分解算法可以看作如下的处理(已经筛出 p [ 1 ] , p [ 2 ] , ⋯   , p [ m a x p ] p[1],p[2],\cdots ,p[maxp] p[1],p[2],,p[maxp]):

n/=p[k]n/=p[k-1]n/p[k-2]……(保证 n   ∣   p n \ | \ p n  p,直到 n==1或n是质数 为止)

我们的时间复杂度瓶颈就在枚举 p [ k ] p[k] p[k] 的循环,即枚举下一个可以被n整除的素数。即使已经筛好了也会慢一些

那,我们有没有可能以 O ( 1 ) O(1) O(1) 的复杂度知道:能被 n n n 整除的下一个最小质因数是几?这样,我们就可以一路除下去找到答案。

这可以实现!因为埃氏筛和欧拉筛的原理都是:通过“素数的倍数一定是合数”这一原理,

那我们只要知道这个“素数”是几,就可以通过创建类似“链式前向星”的数据结构来维护这个除法链。从而 O ( 1 ) O(1) O(1) 实现这一操作

具体实现看代码。比较易懂,只能用欧拉筛,因为埃氏筛复杂度是 O ( n log ⁡ log ⁡ n ) O(n \log \log n) O(nloglogn),会超时。

AC Code

#include <bits/stdc++.h>
using namespace std;
const int maxp = 1e8;

int vis[maxp + 5]; // vis[i]!=0,则表示合数i上一个是从vis[i]转移过来的
int prime[maxp], cnt;

void pretreatment(){ // 预处理出:所有合数是从哪些质数转移过来的
	for(int i = 2; i <= maxp; i ++){
		if(!vis[i]){
			prime[cnt ++] = i;
		}
		for(int j = 0; prime[j] * i <= maxp && j < cnt; j ++){
			vis[prime[j] * i] = prime[j]; // 标记它是从哪里来的,暗示:vis数组里存的都是对答案有用的质数
			if(i % prime[j] == 0) break;
		}
	}
}

void solve(){
	int n; cin >> n;
	if(vis[n] == 0){
		cout << n << '\n'; // 只有一个数,那答案就是它本身
	}
	else{
		int ans = 0; // 赋初始值0是没问题的,因为0异或任何数,结果都是那个非零数
		while(true){
			if(vis[n] == 0){ // 如果终于找到一个质数了,直接退出即可
				ans ^= n;
				break;
			}
			ans ^= vis[n]; // 否则记录答案,因为vis数组里存的一定都是质数
			n /= vis[n]; // 继续转移到下一个合数
		}
		cout << ans << '\n';
	}
}

int main(){
	ios :: sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
	pretreatment();
	int T; cin >> T;
	while(T --) solve();
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

End

谢谢大家!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/爱喝兽奶帝天荒/article/detail/828519
推荐阅读
相关标签
  

闽ICP备14008679号