[Luogu P6091] [Template] Original root

[Template] Original Root

Title link: luogu P6091

General idea

For multiple sets of data, each time n is given, find all its primitive roots.
In order to reduce the output, given a d, you only need to output the numbers in certain positions after the row from small to large.

Ideas

First of all, if you are unclear about the original root, you can take a look at this first:
——>Click me<——

Then I don’t know how to find the smallest primitive root. Look at this:
——>Click me<——

Then at the beginning, you will think about finding the smallest primitive root method to find everything.
Of course, this will time out.

Then we consider what we need to do and optimize.
First of all, let's not talk about finding all, if one count has no primitive roots, then you will waste time running all, which is a waste.
Then you consider which number to find, it has primitive roots.

Then there is a theorem, that is, if a number is a primitive root, then it must satisfy one of these conditions:
it is 1/2/4 1/2/41 / 2 / 4 , or it ispx / 2 pxp ^ x / 2ppx/2px。( p p p is a prime number)

Then you 1/2/41 / 2 / 4 is directly labeled, then primes enumerator, and its power, its power and multiples, look like a flag.
With this method, you can quickly determine whether a number has primitive roots.

Then let's solve the next problem, that is, you can't directly enumerate all judgments whether they are primitive roots.
Here is a method to find all primitive roots through the smallest primitive root.
If you find the smallest primitive root ggg , for allgcd ⁡ (x, φ (n)) \gcd(x,\varphi(n))gcd(x,φ ( n ) )gxg ^ xgx are all primitive roots.
Then you can figure it out and output as required after sorting.

Code

#include<cstdio>
#include<algorithm>
#define ll long long

using namespace std;

int T, prime[1000001], n, d, phi[1000001];
int zyz[1000001], ans[1000001];
bool yes[1000001], np[1000001];

void get_prime() {
    
    //求质数
	for (int i = 2; i <= 1000000; i++) {
    
    
		if (!np[i]) {
    
    
			prime[++prime[0]] = i;
		}
		for (int j = 1; j <= prime[0] && i * prime[j] <= 1000000; j++) {
    
    
			np[i * prime[j]] = 1;
			if (i % prime[j] == 0) break;
		}
	}
}

void check_have() {
    
    //看某个数时候有原根
	yes[1] = 1;
	yes[2] = 1;
	yes[4] = 1;
	for (int i = 1; i <= prime[0]; i++) {
    
    
		ll now = prime[i];
		while (now <= 1000000ll) {
    
    
			yes[now] = 1;
			if (now * 2ll <= 1000000ll) yes[now * 2] = 1;
			now *= 1ll * prime[i];
		}
	}
}

void get_phi() {
    
    //求phi值
	phi[1] = 1;
	for (int i = 2; i <= 1000000; i++) {
    
    
		if (!np[i]) phi[i] = i - 1;
		for (int j = 1; j <= prime[0] && i * prime[j] <= 1000000; j++) {
    
    
			if (i % prime[j] == 0) {
    
    
				phi[i * prime[j]] = phi[i] * prime[j];
				break;
			}
			phi[i * prime[j]] = phi[i] * (prime[j] - 1);
		}
	}
}

void fj(int now) {
    
    //分解质因数
	for (int i = 1; prime[i] * prime[i] <= now; i++)
		if (now % prime[i] == 0) {
    
    
			zyz[++zyz[0]] = prime[i];
			while (now % prime[i] == 0) now /= prime[i];
		}
	if (now > 1) zyz[++zyz[0]] = now;
}

int ksm(ll x, int y, int mo) {
    
    //求快速幂
	ll re = 1ll;
	while (y) {
    
    
		if (y & 1) re = (re * x) % mo;
		x = (x * x) % mo;
		y >>= 1;
	}
	return re;
}

bool check(int x, int p) {
    
    //判断这个数是否是原根
	if (ksm(1ll * x, phi[p], p) != 1) return 0;
	for (int i = 1; i <= zyz[0]; i++)
		if (ksm(1ll * x, phi[p] / zyz[i], p) == 1) return 0;
	return 1;
}

int get_fir(int now) {
    
    //找到第一个原根
	for (int i = 1; i < now; i++) {
    
    //逐个判断,找到就退出
		if (check(i, now)) return i;
	}
	return 0;
}

int gcd(int x, int y) {
    
    //求最大公因子
	if (!y) return x;
	return gcd(y, x % y);
}

void get_all(int fir, int p) {
    
    //得到所有的原根
	int now = 1;
	for (int i = 1; i <= phi[p]; i++) {
    
    
		now = (now * fir) % p;
		if (gcd(i, phi[p]) == 1) ans[++ans[0]] = now;
	}
}

int main() {
    
    
	get_prime();
	
	check_have();
	
	get_phi();
	
	scanf("%d", &T);
	for (int times = 1; times <= T; times++) {
    
    
		scanf("%d %d", &n, &d);
		
		if (!yes[n]) {
    
    
			printf("0\n\n");
		}
		else {
    
    
			printf("%d\n", phi[phi[n]]);
			
			zyz[0] = 0;
			fj(phi[n]);
			
			int fir = get_fir(n);
			
			ans[0] = 0;
			get_all(fir, n);
			
			sort(ans + 1, ans + ans[0] + 1);//判断之后按要求输出
			
			for (int i = 1; i <= phi[phi[n]] / d; i++)
				printf("%d ", ans[i * d]);
			printf("\n");
		}
	}
	
	return 0;
}

Guess you like

Origin blog.csdn.net/weixin_43346722/article/details/114714663