// // // // // // // // // // // // // //

解题营_数论

数论基础

芝士

费马小定理

\(p\) 为质数 则

\[a^{p - 1} \equiv 1 \pmod p \]

欧拉定理

\(a \bot p\)

\[a^{\varphi(p)} \equiv 1 \pmod p \]

其中 \(\varphi(p)\) 表示小于等于 \(p\) 中和 \(p\) 互质的数的个数

\[\varphi(p) = p \times \prod \frac {s_i - 1}{s_i} \]

其中 \(s_i\)\(p\) 的质因数

如果 \(x \bot y\)

\[\varphi(xy) = \varphi(x)\varphi(y) \]

扩展欧拉定理

\[a^b \equiv \begin{cases} a ^ {b \mod \varphi(p)} & \gcd(a, p) = 1\\ a ^ b & \gcd(a, p) \ne p, b < \varphi(p)\\ a ^ {b \mod \varphi(p) + \varphi(p)} & \gcd(a, p) \ne 1, b \geq \varphi(p) \end{cases} \pmod p \]

第三个式子相当于保留最后一个循环节 第一个式子中如果 \(a, p\) 不互质 直接用第一个式子可能会把质数模成 \(0\) 等稀奇古怪的情况

线性筛求欧拉函数

void pre() {
	phi[1] = 1;
    for(int i = 2; i ^ N + 1; i++) {
        if(!vis[i]) prime[++cnt] = i, phi[i] = i - 1;
        for(int j = 1; j ^ cnt + 1 && i * prime[j] <= N; j++)
        {
            vis[i * prime[j]] = 1;
            if(i % prime[j]) phi[i * prime[j]] = phi[i] * phi[prime[j]];
            else {phi[i * prime[j]] = phi[i] * prime[j]; break;}
        }
    }
}

最大公约数与欧几里得算法

对于两个正整数 \(a, b\) 其最大公约数定义为最大的 \(c\) 使 \(c \mid a\)\(c \mid b\)\(\gcd(a, b) = c\)

\[\gcd(a, b) = \gcd(a, b - a) \]

最小公倍数

对于两个正整数 \(a, b\) 其最小公倍数定义为最小的 \(c\) 使 \(a \mid c\)\(b \mid c\)\(lcm(a, b) = c\)

\[lcm(a, b) = \frac {ab}{\gcd(a, b)} \]

对于 \(lcm(a, b, c)\)\(lcm(a, b, c, d)\) ...

\[lcm(S) = \prod_{T \subseteq S, T \ne \varnothing}\gcd(T)^{(-1)^{|T| - 1}} \]

最大公约数与最小公倍数的性质

\[lcm(S) = \prod_{T \subseteq S, T \ne \varnothing}\gcd(T)^{(-1)^{|T| - 1}} \]

证明思路: \(\min\max\) 反演

\[\gcd(Fib_a, Fib_b) = Fib_{\gcd(a, b)} \]

证明思路: \(f_{n + m} = f_mf_{n + 1} + f_{m - 1}f_n\)

\[\gcd(x^a - 1, x^b - 1) = x^{\gcd(a, b)} - 1 \]

证明思路: \(x^a - x^b = x^b(x^{a - b} - 1)\)

扩展欧几里得算法

中国剩余定理

求一元模线性方程组 \(x \equiv a_i \pmod p_i\) 的一个通解 \(p_i\) 两两互质

\(P_i = \frac {\prod_{j = 1}^np_j}{p_i}, t \equiv P_{i}^{-1} \pmod {p_i}\)

构造一个解 \(x \equiv \sum a_it_iP_i \pmod P\)

正确性: \(P_i \mod p_j = 0, t_iP_i \mod p_i = 1\)

整除分块

放到下面题目里面了

卢卡斯定理

如果 \(p\) 为质数 有

\[C_m^n \equiv C_{m \mod p}^{n \mod p} \times C_{\large \lfloor \frac mp \rfloor}^{\large \lfloor \frac np \rfloor} \pmod p \]

线性基

异或

\(01\)

高斯消元

题目

上帝与集合的正确用法

\[2^{2^{2^{2^{2^{\dots}}}}} \mod p \]

\(1 \leq T \leq 10^3, 1 \leq p \leq 10^7\)

先特判 \(1\)\(2\)

扩展欧拉定理

把右上角那一坨东西设为 \(a\)

\[2^a \equiv 2^{a \mod \varphi(p) + \varphi(p)} \pmod p \]

问题由一坨东西模 \(p\) 变为了一坨东西模 \(\varphi(p)\)

对于 \(\varphi\) 函数 每两次至少跌落一半 当 \(\varphi\) 里面变成 \(1\) 之后就不需要的 也就是递归出口 所以复杂度为 \(\log\) 级别

代码

/*
  Source: 
*/
#include<cstdio>
#define int long long
#define pn putchar('\n')
/*----------------------------------------------------------*/
const int D = 1e7 + 7;
/*----------------------------------------------------------*/
int T, phi[D], prime[D], cnt;
bool vis[D];
/*----------------------------------------------------------*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void Print(int x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
/*----------------------------------------------------------*/
void pre() {
	phi[1] = 1;
	for(int i = 2; i ^ D; i++)
	{
		if(!vis[i]) prime[++cnt] = i, phi[i] = i - 1;
		for(int j = 1; j ^ cnt + 1 && i * prime[j] < D; j++)
		{
			vis[i * prime[j]] = 1;
			if(i % prime[j]) phi[i * prime[j]] = phi[i] * phi[prime[j]];
			else {phi[i * prime[j]] = phi[i] * prime[j]; break;}
		}
	}
}
int power(int x, int y, int mod) {int res = 1; for(; y; x = x * x % mod, y >>= 1) if(y & 1) res = res * x % mod; return res;}
int solve(int p) {
	if(p == 1) return 0;
	return power(2, solve(phi[p]) + phi[p], p);
}
/*----------------------------------------------------------*/
signed main() {
	pre(); T = read(); while(T--) Print(solve(read())), pn;
	return 0;
}

互质的数的和

给定 \(n\)\(1\)\(n\) 中与 \(n\) 互质的数的和

\(n \leq 10^{14}\)

由于 \(\gcd(a, n) = \gcd(n - a, n)\)

所以将与 \(n\) 互质的数放在一起 收尾配对 一对数的和为 \(n\)

因此答案为 \(n \times \frac {\varphi(n)}2\)

仪仗队

\[\sum_{i = 1}^n\sum_{j = 1}^n[\gcd(i, j) = 1] \]

\(1 \leq n \leq 4 \times 10^ 4\)

求互质的数的对数

\[\sum_{i = 1}^n\sum_{j = 1}^n[\gcd(i, j) = 1] = 2\sum_{i = 1}^n\varphi(i) - 1 \]

\(\varphi\) 的前缀和

代码

/*
  Source: P2158 [SDOI2008] 仪仗队
*/
#include<cstdio>
/*----------------------------------------------------------*/
const int A = 4e4 + 7;
/*----------------------------------------------------------*/
int n, phi[A], prime[A], cnt, ans;
bool vis[A];
/*----------------------------------------------------------*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void Print(int x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
/*----------------------------------------------------------*/

/*----------------------------------------------------------*/
int main() {
	n = read(); if(n == 1) {Print(0); return 0;} phi[1] = 1;
	for(int i = 2; i ^ n + 1; i++)
	{
		if(!vis[i]) prime[++cnt] = i, phi[i] = i - 1;
		for(int j = 1; j ^ cnt + 1 && i * prime[j] <= n; j++)
		{
			vis[i * prime[j]] = 1;
			if(i % prime[j]) phi[i * prime[j]] = phi[i] * phi[prime[j]];
			else {phi[i * prime[j]] = phi[i] * prime[j]; break;}
		}
	}
	for(int i = 1; i ^ n; i++) ans += phi[i];
	Print(ans << 1 | 1);
	return 0;
}

Prime Swaps

给出一个长为 \(n\) 的排列 每次选择两个位置 \(i, j\) 并交换上面两个数 前提是 \(j - i +1\) 为质数

要求在 \(5n\) 次操作内 将这个序列拍好 输出具体排序的操作

\(n \leq 10^5\)

哥德巴赫猜想 任一大于 \(2\) 的偶数 都可以表示为两个素数的和

那么每次将一个数向前移动 \(x\) 距离时 先移动一个尽量打的质数的距离即可

贪心从 \(1, 2, 3, 4, \dots\) 的顺序排即可

然后由于质数的密度为 \(\ln(n)\) 所以可以保证在 \(5n\) 次一定可以完成

GCD

\[\sum_{i = 1}^n\sum_{j = 1}^n[\gcd(i, j)\ is\ prime] \]

\(1 \leq n \leq 10^7\)

对于每一个质数 对答案的贡献就是

\[\sum_{i = 1}^n\sum_{j = 1}^n[\gcd(i, j)\ is\ prime] = \sum_{k\ is\ prime}^n\sum_{i = 1}^{\lfloor \frac nk \rfloor}\sum_{j = 1}^{\lfloor \frac nk \rfloor}[\gcd(i, j) = 1]\\ = \sum_{k\ is\ prime}^n \left(2\sum_{i = 1}^{\lfloor \frac nk \rfloor}\varphi(i) - 1\right) \]

预处理 \(\varphi\) 函数 求前缀和 枚举所有质数 对每个质数 \(O(1)\) 统计贡献

代码

/*
  Source: P2568 GCD
*/
#include<cstdio>
#define int long long
/*----------------------------------------------------------*/
const int D = 1e7 + 7;
/*----------------------------------------------------------*/
int n, phi[D], prime[D], cnt, sum[D], ans;
bool vis[D];
/*----------------------------------------------------------*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void Print(int x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
/*----------------------------------------------------------*/
void pre() {
	phi[1] = 1;
	for(int i = 2; i ^ n + 1; i++)
	{
		if(!vis[i]) prime[++cnt] = i, phi[i] = i - 1;
		for(int j = 1; j ^ cnt + 1 && i * prime[j] <= n; j++)
		{
			vis[i * prime[j]] = 1;
			if(i % prime[j]) phi[i * prime[j]] = phi[i] * phi[prime[j]];
			else {phi[i * prime[j]] = phi[i] * prime[j]; break;}
		}
	}
	for(int i = 1; i ^ n + 1; i++) sum[i] = sum[i - 1] + phi[i];
}
/*----------------------------------------------------------*/
signed main() {
	n = read(); pre();
	for(int i = 1; i ^ cnt + 1 && prime[i] <= n; i++) ans += (sum[n / prime[i]] << 1) - 1;
	Print(ans);
	return 0;
}

互质对数

给定正整数 \(n\) 求多少个二元组 \((i, j)\) 满足

  • \(1 \leq i < \leq n\)
  • \(j + i\)\(j - i\) 互质

\(n \leq 10^7, T \leq 10^5\)

\[\sum_{i = 1}^n\sum_{j = 1}^n[\gcd(j - i, j + 1) = 1] \]

\[\sum_{i = 1}^n\sum_{j = 1}^{i - 1}[\gcd(2i, i + j) = 1] \]

如果令 \(t = j - i\) 再枚举一个 \(j\) 则问题转化为有多少对 \((i, t)\) 满足 \(i + t = j\)\(\gcd(i, t) = 1\) 同时 \(t\) 为奇数

\(j\) 的奇偶讨论

  • \(j\) 为偶数 此时 \((i, t)\) 的对数为 \(\varphi(j)\)
  • \(j\) 为奇数 此时 \((i, t)\) 的对数为 \(\frac {\varphi(j)}2\)

上面那个是因为 \(i, t\) 都是奇数 可以互换

下面那个是因为 一奇一偶 一个分给 \(i\) 一个分给 \(t\)

预处理上面的前缀和 每次 \(O(1)\) 回答

Longge的问题

\[\sum_{i = 1}^n\gcd(i, n) \]

\(1 \leq n \leq 2^{32}\)

\[\sum_{i = 1}^n\gcd(i, n)\\ = \sum_{d \mid n}\sum_{i = 1}^{\frac nd }[\gcd \left(i, \frac nk\right) = 1]\\ = \sum_{d \mid n}d \varphi\left(\frac nd \right) \]

枚举所有的 \(d\) 质因数分解硬求 \(\varphi\)

代码

/*
  Source: P2303 [SDOI2012] Longge 的问题
*/
#include<cstdio>
#define int long long
/*----------------------------------------------------------*/
int n, ans;
/*----------------------------------------------------------*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void Print(int x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
/*----------------------------------------------------------*/
int phi(int x) {
	int res = x;
	for(int i = 2; i * i <= x; i++)
	{
		if(!(x % i)) res = res / i * (i - 1);
		while(!(x % i)) x /= i;
	}
	if(x > 1) res = res / x * (x - 1);
	return res;
}
/*----------------------------------------------------------*/
signed main() {
	n = read();
	for(int i = 1; i * i <= n; i++) if(!(n % i))
		ans += i * phi(n / i) + (n / i != i) * (n / i) * phi(i);
	Print(ans);
	return 0;
}

奇数国

一个长度为 \(n\) 的序列 每个数都是前 \(60\) 小的质数之一 两种操作一共 \(M\)

  • 修改一个位置的数为某前 \(60\) 小的质数之一
  • 查询一个区间乘积的 \(\varphi\)

\(10^9 + 7\) 取模

\(1 \leq n, m \leq 10^5\)

线段树 考虑维护什么

单点修改可以直接改

需要维护区间乘积的 \(\varphi\)

维护一个区间乘积 维护有哪些质因数 质因数只有 \(60\) 个 压一下

可以直接合并 乘积直接相乘 质因数或一下 根据定义维护 \(\varphi\) 值 直接暴力维护

代码

/*
  Source: P4140 奇数国
*/
#include<cstdio>
#define int long long
#define pn putchar('\n')
/*----------------------------------------------------------*/
const int B = 1e5 + 7;
const int mod = 19961993;
/*----------------------------------------------------------*/
int n, prime[70], cnt, inv[70];
bool vis[300];
/*----------------------------------------------------------*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void Print(int x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
/*----------------------------------------------------------*/
void pre() {
	for(int i = 2; i ^ 282; i++)
	{
		if(!vis[i]) prime[++cnt] = i;
		for(int j = 1; j ^ cnt + 1 && i * prime[j] <= 281; j++)
		{
			vis[i * prime[j]] = 1;
			if(!(i % prime[j])) break;
		}
	}
}
int power(int x, int y) {int res = 1; for(; y; x = x * x % mod, y >>= 1) if(y & 1) res = res * x % mod; return res;}
namespace Seg {
	#define ls(x) x << 1
	#define rs(x) x << 1 | 1
	#define mid (t[p].l + t[p].r >> 1)
	struct node {int l, r, prod, S;} t[B << 2];
	void f(int p, int k) {
		t[p].prod = k; int s = 0;
		for(int i = 1; i ^ cnt + 1; i++)
			if(!(k % prime[i])) s |= 1ll << i - 1;
		t[p].S = s;
	}
	node operator + (node x, node y) {
		node z; z.l = x.l; z.r = y.r;
		z.prod = x.prod * y.prod % mod;
		z.S = x.S | y.S; return z;
	}
	void build(int p, int l, int r) {
		t[p].l = l; t[p].r = r; if(l == r) {f(p, 3); return ;}
		build(ls(p), l, mid); build(rs(p), mid + 1, r);
		t[p] = t[ls(p)] + t[rs(p)];
	}
	void up_date(int p, int pos, int k) {
		if(t[p].l == pos && t[p].r == pos) {f(p, k); return ;}
		if(pos <= mid) up_date(ls(p), pos, k); else up_date(rs(p), pos, k);
		t[p] = t[ls(p)] + t[rs(p)];
	}
	node query(int p, int l, int r) {
		if(l <= t[p].l && t[p].r <= r) return t[p];
		if(l <= mid) if(r > mid) return query(ls(p), l, r) + query(rs(p), l, r);
		else return query(ls(p), l, r); else return query(rs(p), l, r);
	}
}
void Main() {
	pre(); n = read(); Seg::build(1, 1, 1e5);
	for(int i = 1; i ^ cnt + 1; i++) inv[i] = power(prime[i], mod - 2) % mod;
	for(int i = 1; i ^ n + 1; i++)
		if(read())
		{
			int x = read(), y = read();
			Seg::up_date(1, x, y);
		}
		else
		{
			int x = read(), y = read(); Seg::node tmp = Seg::query(1, x, y);
			int sum = tmp.prod, s = tmp.S;
			for(int i = 1; i ^ cnt + 1; i++) if(1ll << i - 1 & s)
				sum = sum * inv[i] % mod * (prime[i] - 1) % mod;
			Print(sum); pn;
		}
}
/*----------------------------------------------------------*/
signed main() {Main(); return 0;}

pre

给一个序列 \(s\) (可能有重复的元素 而相同元素交换位置依然是同一个排列) 求 \(s\) 的排名 对 \(m\) 取模

\(n \leq 3 \times 10^5\)

\(m\) 不一定为质数

跑路了...

对模数不是质数 将模数进行分解 转化为模 \(p^k\) 其中 \(p\)\(m\) 的质因数

在对 \(p\) 取模的运算中 需要记录 \(k = xp^y\) 中的 \(x\)\(y\)

其中 \(x\)\(p\) 取模 \(y\)\(\varphi(p)\) 取模 最后 CRT 起来

整数分块

\[\sum_{i = 1}^n\left(\lfloor\frac ni \rfloor\right)^5 \times i \]

\(n \leq 10^9\) 答案对 \(10^9 + 7\) 取模

\(\lfloor\frac ni \rfloor\) 只有 \(O(\sqrt n)\) 种取值 遍历每个取值相同的区间即可

常用来优化复杂度 积性函数的题目

比如上面这个题目中 设 \(f_a = a^5, sum_i = \frac {(i + 1)i}2\)

把上面那个东西写成整除分块的形式 大概就是这样的一坨东西:

\[\sum_{i = 1}^n\left(\large \left \lfloor\frac ni \right \rfloor \right)^5 \times i\\ = \sum_{i = 1}^{\lfloor\sqrt n \rfloor}i^5 \times \left(\frac{i(i + 1)}2 - \frac {i(i - 1)}2 \right) \]

示例代码

for(int i = 1, last; i <= n; i = last + 1)
{
    int a = n / i; last = n / a;//区间终点
    ans += f(a) * (sum(last) - sum(i - 1));
}

例题

\[\sum_{a = 1}^n\sum_{b = 1}^n\gcd(x^a - 1, x^b - 1) \]

\(x, n \leq 10^6, T \leq 10^3\) 答案对 \(10^9 + 7\) 取模

化:

\[ans = \sum_{a = 1}^n\sum_{b = 1}^n\left(x^{\gcd(a, b)} - 1\right)\\ = \sum_{k = 1}^n(x^k - 1)\sum_{a = 1}^n\sum_{b = 1}^n[\gcd(a, b) = k]\\ = \sum_{k = 1}^n(x^k - 1)\sum_{a = 1}^{\lfloor \frac nk \rfloor}\sum_{b = 1}^{\lfloor \frac nk \rfloor}[\gcd(a, b) = 1]\\ = \sum_{k = 1}^n(x^k - 1)\left(2\sum_{i = 1}^{\lfloor \frac nk \rfloor}\varphi(i) - 1 \right) \]

对后面那一坨东西预处理前缀和 可以枚举 \(k\) 对每一次询问 \(O(n)\) 的询问

有一千组询问 显然可以超时

后面那一坨东西只与 \(k\) 有关 且在一段里面是相同的

整除分块(等比数列化简) :

\[\sum_{k = 1}^n(x^k - 1)\left(2\sum_{i = 1}^{\lfloor \frac nk \rfloor}\varphi(i) - 1 \right)\\ = \sum_{i = 1}^{\lfloor\sqrt n\rfloor}\left(\left(\frac {x(x^i - 1)}{x - 1} - i \right) - \left(\frac {x(x^{i - 1} - 1)}{x - 1} - (i - 1) \right) \right) \times \left(2\sum_{j = 1}^i \varphi(j) - 1 \right) \]

\(\varphi\) 处理前缀和之后 每次询问 \(O(\sqrt n)\)

古代猪文

给定 \(N, k, G\)

\[G^{\sum_{k \mid N}{N \choose k}} \pmod {999911658} \]

\(n \leq 10^9\)

强行卢卡斯 + 强行 CRT

给的 \(n\) 范围奇大 强行卢卡斯

模数不给质数 强行 CRT

出题人不是东西 石锤了

枚举 \(k\) 卢卡斯求组合数 最后 CRT 起来

/*
  Source: P2480 [SDOI2010]古代猪文
*/
#include<cstdio>
#include<cstring>
#define int long long
#define pt putchar(' ')
#define pn putchar('\n')
#define Abs(x) ((x) < 0 ? -(x) : (x))
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*----------------------------------------------------------*/
const int A = 5e4 + 7;
const int B = 1e5 + 7;
const int D = 1e7 + 7;
const int mod = 999911658;
const int INF = 0x3f3f3f3f;
/*----------------------------------------------------------*/
inline void File() {
	freopen(".in", "r", stdin);
	freopen(".out", "w", stdout);
}
/*----------------------------------------------------------*/
int n, G, fac[A], a[5], b[5] = {0, 2, 3, 4679, 35617}, ans;
/*----------------------------------------------------------*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void Print(int x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
/*----------------------------------------------------------*/
int power(int x, int y, int p) {int res = 1; for(; y; x = x * x % p, y >>= 1) if(y & 1) res = res * x % p; return res;}
void pre(int p) {fac[0] = 1; for(int i = 1; i ^ p + 1; i++) fac[i] = fac[i - 1] * i % p;}
int C(int n, int m, int p) {if(n < m) return 0; return fac[n] * power(fac[m], p - 2, p) % p * power(fac[n - m], p - 2, p) % p;}
int lucas(int n, int m, int p) {if(n < m) return 0; if(!n) return 1; return lucas(n / p, m / p, p) * C(n % p, m % p, p) % p;}
void crt() {for(int i = 1; i ^ 5; i++) ans = (ans + a[i] * (mod / b[i]) % mod * power(mod / b[i], b[i] - 2, b[i]) % mod);}
void Main() {
	n = read(); G = read(); if(!(G % (mod + 1))) {Print(0); return ;}
	for(int k = 1; k ^ 5; k++)
	{
		pre(b[k]);
		for(int i = 1; i * i <= n; i++) if(!(n % i))
		{
			a[k] = (a[k] + lucas(n, i, b[k]) % b[k]) % b[k];
			if(i * i != n) a[k] = (a[k] + lucas(n, n / i, b[k]) % b[k]) % b[k];
		}
	}
	crt(); Print(power(G, ans, mod + 1));
}
/*----------------------------------------------------------*/
signed main() {Main(); return 0;}

posted @ 2021-08-20 22:45  Blank_space  阅读(120)  评论(2编辑  收藏  举报
// // // // // // //