【简单数论题】整除

Description

  • 给定 n , m n,m n,m, 求 [ 1 , n ] [1,n] [1,n] 内满足 n ∣ x m − x n|x^m-x nxmx x x x 的个数。
  • m ≤ 1 0 9 m\le 10^9 m109
  • n = ∏ i = 1 c p i n=\prod_{i=1}^c p_i n=i=1cpi p i p_i pi 均为质数且互不相同, p i ≤ 10000 , c ≤ 50 p_i\le 10000,c\le50 pi10000,c50
  • 每个测试点数据组数 ≤ 10000 \le 10000 10000
  • 时间限制 5 s 5s 5s,空间限制 256 M B 256MB 256MB

Solution

  • 非常有意思的一道题。
  • 感觉让我这个初学数论的菜鸡学到了很多东西。

算法一

  • 原方程等价于 x ( x m − 1 − 1 ) ≡ 0 ( m o d   n ) x(x^{m-1}-1)\equiv 0(mod~n) x(xm11)0(mod n)
  • x x x 的取值范围可以对应到 [ 0 , n ) [0,n) [0,n),这样我们只需要判断模 n n n 意义下的解的个数即可。
  • 这个同余方程成立,等价于对于每个 i i i x ( x m − 1 − 1 ) ≡ 0 ( m o d   p i ) x(x^{m-1}-1)\equiv 0(mod~p_i) x(xm11)0(mod pi)
  • 由于 p i p_i pi 是质数,我们可以知道上面这个方程又等价于 x ≡ 0 ( m o d   p i ) x\equiv 0(mod~p_i) x0(mod pi) x m − 1 ≡ 1 ( m o d   p i ) x^{m-1}\equiv 1(mod~p_i) xm11(mod pi)
  • 上面两个方程解出来的 x x x 均是 p i p_i pi剩余类
  • 那么我们就取模 p p p 的值作为方程的解。
  • 显然两个方程解出的 x x x 不会有同一个。
  • 所以方程 x ( x m − 1 − 1 ) ≡ 0 ( m o d   p i ) x(x^{m-1}-1)\equiv 0(mod~p_i) x(xm11)0(mod pi) 的解数就是上面两个方程的解数之和。
  • 我们假设 A i A_i Ai 表示我们对方程 x ( x m − 1 − 1 ) ≡ 0 ( m o d   p i ) x(x^{m-1}-1)\equiv 0(mod~p_i) x(xm11)0(mod pi) 解出的所有 x x x p i p_i pi 的值组成的集合。
  • 那么,因为 p i p_i pi 互不相同,根据中国剩余定理,我们对于每个同余方程组:

∀ i ∈ [ 1 , c ] , x ≡ a i ( m o d   p i ) , a i ∈ A i \forall i\in[1,c],x\equiv a_i(mod~p_i),a_i\in A_i i[1,c]xai(mod pi)aiAi

  • 我们都能得到模 n n n 意义下的唯一解。
  • 所以解数就是所有集合 A i A_i Ai 的大小的乘积
  • 我们回到刚刚的两个方程: x ≡ 0 ( m o d   p i ) x\equiv 0(mod~p_i) x0(mod pi) x m − 1 ≡ 1 ( m o d   p i ) x^{m-1}\equiv 1(mod~p_i) xm11(mod pi)
  • 对于方程1,我们只需要取 x = 0 x=0 x=0 一个解。
  • 对于方程2,暴力枚举验证的每次时间复杂度是 O ( p i log ⁡ m ) O(p_i\log m) O(pilogm) 的。
  • 但是时间复杂度为 O ( T ∑ i = 1 c p i log ⁡ m ) O(T\sum_{i=1}^c p_i\log m) O(Ti=1cpilogm),并不能通过本题。

算法二

  • 现在的问题就是如何快速求出形如 x n ≡ 1 ( m o d   p ) , p 是 质 数 x^n \equiv 1(mod~p),p是质数 xn1(mod p)p
  • 的方程在模 p p p 意义下的解的数量。
  • 我们定义数论函数 f ( x ) = x n f(x)=x^n f(x)=xn
  • 显然 f ( x ) f(x) f(x) 是完全积性函数。
  • 我们可以使用线性筛线性筛出,对于每个质数暴力快速幂计算。
  • [ 1 , p ) [1,p) [1,p) 内质数个数是 O ( p ln ⁡ p ) O(\frac{p}{\ln p}) O(lnpp) 的,然后对于每个质数 O ( log ⁡ n ) O(\log n) O(logn) 计算,总的时间复杂度可以认为是 O ( p ) O(p) O(p),然后线性筛部分是 O ( p ) O(p) O(p) 的。
  • 因此我们可以每次 O ( p ) O(p) O(p) 处理出来这个函数在 [ 0 , p ) [0,p) [0,p) 上的所有值对 p p p 取模的值,并 O ( p ) O(p) O(p) 求出解的数量,实现 O ( T ∑ i = 1 c p i ) O(T\sum_{i=1}^c p_i) O(Ti=1cpi) 的时间复杂度。
#include <cmath>
#include <cstdio>
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>

template <class T>
inline void read(T &x)
{
	static char ch; 
	while (!isdigit(ch = getchar())); 
	x = ch - '0'; 
	while (isdigit(ch = getchar()))
		x = x * 10 + ch - '0'; 
}

const int MaxN = 1e4 + 5; 

int id, T, c, m, n; 

inline int qpow(int a, int b, const int &mod)
{
	int res = 1; 
	for (; b; b >>= 1, a = 1LL * a * a % mod)
		if (b & 1)
			res = 1LL * res * a % mod; 
	return res; 
}

//the number of x (x^m=p)(x in [0,p))
inline int solve(const int &m, const int &p)
{
	static bool sie[MaxN]; 
	static int pri[MaxN], f[MaxN], cnt; 
	
	int res = 1; 
	
	memset(sie, 0, sizeof(sie)); 
	cnt = 0; 
	
	f[1] = 1; 
	for (int i = 2; i < p; ++i)
	{
		if (!sie[i])
		{
			pri[++cnt] = i; 
			f[i] = qpow(i, m, p); 
		}
		for (int j = 1; j <= m; ++j)
		{
			int x = i * pri[j]; 
			if (x >= p) break; 
			sie[x] = true; 
			
			f[x] = 1LL * f[i] * f[pri[j]] % p; 
			if (i % pri[j] == 0)
				break; 
		}
		if (f[i] == 1)
			++res; 
	}
	return res; 
}

int main()
{
	freopen("division.in", "r", stdin); 
	freopen("division.out", "w", stdout); 
	
	#define mod 998244353
	
	read(id), read(T); 
	while (T--)
	{
		read(c), read(m); 
		
		int ans = 1; 
		
		int x; 
		for (int i = 1; i <= c; ++i)
		{
			read(x); 
			ans = 1LL * ans * (solve(m - 1, x) + 1) % mod; 
		}
		
		printf("%d\n", ans); 
	}
	
	fclose(stdin); 
	fclose(stdout); 
	return 0; 
}

算法三

  • 实际上这道题有很优秀的做法。
  • 此算法来自某个神仙考场上猜的结论。
  • 方程 x n ≡ 1 ( m o d   p ) , p 是 质 数 x^n \equiv 1(mod~p),p是质数 xn1(mod p)p 在模 p p p 意义下的解的数量为 g c d ( n , p − 1 ) gcd(n,p-1) gcd(n,p1)
  • 我们来证明一下这个显而易见的结论。
  • 证明:
    • x x x 的取值范围为 [ 1 , p ) [1,p) [1,p)
    • 这个方程形如一个 N N N 次剩余的形式。
    • 我们令 g g g 表示 p p p 的一个原根。
    • 根据原根的性质 ,集合 { x ∣ x = g k &VeryThinSpace; m o d &VeryThinSpace; p , k ∈ [ 1 , p ) , k ∈ Z } \{x|x=g^k\bmod p,k\in [1,p),k\in \mathbb Z\} {xx=gkmodp,k[1,p),kZ} 和集合 { 1 , 2 , … , p − 1 } \{1,2,\dots,p-1\} {1,2,,p1} 相等。
    • x = g y x=g^y x=gy y ∈ [ 1 , p ) y\in[1,p) y[1,p))。
    • 所以原方程可以表示为 g n × y ≡ g 0 ( m o d   p ) g^{n\times y}\equiv g^0(mod~p) gn×yg0(mod p)
    • 根据 g p − 1 = 1 g^{p-1}=1 gp1=1,我们得到 n × y ≡ 0 ( m o d   ( p − 1 ) ) n\times y\equiv 0(mod~(p-1)) n×y0(mod (p1))
    • 写成同余方程的形式即 ( p − 1 ) x + n y = 0 (p-1)x+ny=0 (p1)x+ny=0
    • 显然我们可以取特解 x = y = 0 x=y=0 x=y=0,得到 y y y 的通解为 k ⋅ p − 1 ( p − 1 , n ) ( k ∈ Z ) k·\frac{p-1}{(p-1,n)}(k\in \mathbb Z) k(p1,n)p1kZ
    • 我们代到 y y y 的取值范围中即有 1 ≤ k ⋅ p − 1 ( p − 1 , n ) ≤ p − 1 1\le k·\frac{p-1}{(p-1,n)}\le p-1 1k(p1,n)p1p1
    • 解出来 k k k 可以取 [ 1 , ( p − 1 , n ) ] [1,(p-1,n)] [1,(p1,n)] 内的任意整数。
    • 证毕。
  • 那么对于每个质数就可以 O ( l o g   p ) O(log~p) O(log p) 的时间复杂度求出 g c d gcd gcd 算答案了。
  • 答案即为 ∏ i = 1 c ( g c d ( m − 1 , p − 1 ) + 1 ) \prod_{i=1}^c(gcd(m-1,p-1)+1) i=1c(gcd(m1,p1)+1)
  • 可以轻松通过本题。
#include <cmath>
#include <cstdio>
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>

const int mod = 998244353; 

int id, T, c, m, n; 

int main()
{
	freopen("division.in", "r", stdin); 
	freopen("division.out", "w", stdout); 
	
	scanf("%d%d", &id, &T); 
	while (T--)
	{
		scanf("%d%d", &c, &m); 
		
		int ans = 1; 
		
		int x; 
		for (int i = 1; i <= c; ++i)
		{
			scanf("%d", &x); 
			ans = 1LL * ans * (1 + std::__gcd(m - 1, x - 1)) % mod; 
		}
		
		std::cout << ans << std::endl; 
	}
	
	fclose(stdin); 
	fclose(stdout); 
	return 0; 
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值