【集训】简单数论
简单数论
P8255 [NOI Online 2022 入门组] 数学游戏
若 \(x\nmid z\),则一定无解。
令 \(x=d\times a,y=d\times b\),其中 \(\gcd(a,b)=1\)。那么有 \(\gcd(x,y)=d\)。则 \(z=x\times y\times \gcd(x,y)=d^3\times a\times b\)。
我们知道 \(x\) 与 \(z\),那么可以求出 \(\frac{z}{x}=d^2\times b\)。由于 \(\gcd(a,b)=1\),所以 \(\gcd(\frac{z}{x},x^2)=\gcd(d^2\times b,d^2\times a^2)=d^2\)。若 \(\gcd(\frac{z}{x},x^2)\) 不是完全平方数,则无解。
将 \(\gcd(\frac{z}{x},x^2)\) 开方即可得到 \(d\)。那么:\(\frac{\frac{z}{x}}{\sqrt{\gcd(\frac{z}{x},x^2)}}=\frac{d^2\times b}{d}=d\times b=y\)所以我们有:\(y=\frac{\frac{z}{x}}{\sqrt{\gcd(\frac{z}{x},x^2)}}\)
时间复杂度 \(\mathcal{O}(T\log x)\)。
#include <bits/stdc++.h>
using namespace std;
#define FOR(i, a, b) for (int i = (a); i <= (b); ++i)
#define ROF(i, a, b) for (int i = (a); i >= (b); --i)
#define DEBUG(x) cerr << #x << " = " << x << endl
#define ll long long
typedef pair <int, int> PII;
typedef unsigned int uint;
typedef unsigned long long ull;
#define i128 __int128
#define fi first
#define se second
mt19937 rnd(chrono::system_clock::now().time_since_epoch().count());
#define ClockA clock_t start, end; start = clock()
#define ClockB end = clock(); cerr << "time = " << double(end - start) / CLOCKS_PER_SEC << "s" << endl;
//#define int long long
inline int rd(){
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 - '0', ch = getchar();
return x * f;
}
#define rd rd()
void wt(int x){
if (x < 0)
putchar('-'), x = -x;
if (x > 9)
wt(x / 10);
putchar(x % 10 + '0');
return;
}
void wt(char x){
putchar(x);
}
void wt(int x, char k){
wt(x),putchar(k);
}
namespace Star_F{
ll x, t;
void Main(){
cin >> x >> t;
if (t % x != 0) {
cout << -1 << endl;
return;
}
t /= x;
ll g = __gcd(x * x, t), s = sqrt(g) + 0.5;
if (s * s != g) {
cout << -1 << endl;
return;
}
cout << t / s << endl;
}
}
signed main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
ClockA;
int T=1;
T=rd;
while(T--) Star_F::Main();
// ClockB;
return 0;
}
/*
* ▀▀▀██████▄▄▄ _______________
* ▄▄▄▄▄ █████████▄ / \
* ▀▀▀▀█████▌ ▀▐▄ ▀▐█ | Code has no BUG! |
* ▀▀█████▄▄ ▀██████▄██ | _________________/
* ▀▄▄▄▄▄ ▀▀█▄▀█════█▀ |/
* ▀▀▀▄ ▀▀███ ▀ ▄▄
* ▄███▀▀██▄████████▄ ▄▀▀▀▀▀▀█▌ ______________________________
* ██▀▄▄▄██▀▄███▀ ▀▀████ ▄██ █ \\
* ▄▀▀▀▄██▄▀▀▌████▒▒▒▒▒▒███ ▌▄▄▀▀▀▀█_____________________________ //
* ▌ ▐▀████▐███▒▒▒▒▒▐██▌
* ▀▄▄▄▄▀ ▀▀████▒▒▒▒▄██▀
* ▀▀█████████▀
* ▄▄██▀██████▀█
* ▄██▀ ▀▀▀ █
* ▄█ ▐▌
* ▄▄▄▄█▌ ▀█▄▄▄▄▀▀▄
* ▌ ▐ ▀▀▄▄▄▀
* ▀▀▄▄▀ ██
* \ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▀
* \- ▌ Name: Star_F ▀ ▀
* - ▌ (o) ▀
* /- ▌ Go Go Go ! ▀ ▀
* / ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▀
*/
P1082 [NOIP 2012 提高组] 同余方程
exgcd
模板题
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
int exgcd(int a, int b, int &x, int &y){
if (!b){
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main(){
int a, b;
cin >> a >> b;
int x, y;
exgcd(a, b, x, y);
cout << (x % b + (LL)b) % b << endl;
return 0;
}
P1390 公约数的和
假设先求这个:\(\text{ans}=\sum_{k=1}^nk*f_k\)
-
\(f_k\) 表示 \(\gcd(i,j)=k\) 的个数
-
\(g_k\) 表士 \(k|\gcd(i,j)\) 的个数,显然 \(g_k=f_k+f_{2k}+\cdots+f_{mk}\)
而 \(g_k=\lfloor\frac nk\rfloor^2 \ ( i,j分别由有\lfloor\frac nk\rfloor种选择)\)
所以 \(f[k]=\lfloor\frac nk\rfloor^2-(f_{2k}+f_{3k}+\cdots)\)
复杂度是 \(n\times (1+\frac12+\frac13+...+\frac1n)\)
所以总复杂的为 \(\mathcal{O}(nH(n))\),\(H(n)\) 为调和级数
因为 \(n\) 不大,这个算法常数小,所以就比算 \(\sum_{d=1}^n\phi(d)*\lfloor\frac nd\rfloor^2\) 要快一些
而本题就等于:
#include <bits/stdc++.h>
using namespace std;
#define FOR(i, a, b) for (int i = (a); i <= (b); ++i)
#define ROF(i, a, b) for (int i = (a); i >= (b); --i)
#define DEBUG(x) cerr << #x << " = " << x << endl
#define ll long long
typedef pair <int, int> PII;
typedef unsigned int uint;
typedef unsigned long long ull;
#define i128 __int128
#define fi first
#define se second
mt19937 rnd(chrono::system_clock::now().time_since_epoch().count());
#define ClockA clock_t start, end; start = clock()
#define ClockB end = clock(); cerr << "time = " << double(end - start) / CLOCKS_PER_SEC << "s" << endl;
//#define int long long
inline int rd(){
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 - '0', ch = getchar();
return x * f;
}
#define rd rd()
void wt(int x){
if (x < 0)
putchar('-'), x = -x;
if (x > 9)
wt(x / 10);
putchar(x % 10 + '0');
return;
}
void wt(char x){
putchar(x);
}
void wt(int x, char k){
wt(x),putchar(k);
}
namespace Star_F{
ll n,ans,f[2000010];
void Main(){
cin >> n;
for (int i = n; i; --i){
f[i] = n / i * (n / i);
for (int j = i << 1; j <= n; j += i)
f[i] -= f[j];
ans += f[i] * i;
}
cout << (ans - n * (n + 1) / 2 >> 1) << endl;
}
}
signed main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
ClockA;
int T=1;
// T=rd;
while(T--) Star_F::Main();
// ClockB;
return 0;
}
/*
* ▀▀▀██████▄▄▄ _______________
* ▄▄▄▄▄ █████████▄ / \
* ▀▀▀▀█████▌ ▀▐▄ ▀▐█ | Code has no BUG! |
* ▀▀█████▄▄ ▀██████▄██ | _________________/
* ▀▄▄▄▄▄ ▀▀█▄▀█════█▀ |/
* ▀▀▀▄ ▀▀███ ▀ ▄▄
* ▄███▀▀██▄████████▄ ▄▀▀▀▀▀▀█▌ ______________________________
* ██▀▄▄▄██▀▄███▀ ▀▀████ ▄██ █ \\
* ▄▀▀▀▄██▄▀▀▌████▒▒▒▒▒▒███ ▌▄▄▀▀▀▀█_____________________________ //
* ▌ ▐▀████▐███▒▒▒▒▒▐██▌
* ▀▄▄▄▄▀ ▀▀████▒▒▒▒▄██▀
* ▀▀█████████▀
* ▄▄██▀██████▀█
* ▄██▀ ▀▀▀ █
* ▄█ ▐▌
* ▄▄▄▄█▌ ▀█▄▄▄▄▀▀▄
* ▌ ▐ ▀▀▄▄▄▀
* ▀▀▄▄▀ ██
* \ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▀
* \- ▌ Name: Star_F ▀ ▀
* - ▌ (o) ▀
* /- ▌ Go Go Go ! ▀ ▀
* / ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▀
*/
P1516 青蛙的约会
求:\(x+km\equiv y+kn\pmod{l}\) ,其中 \(k\) 为所求。
化简:
我们设 \(S=x-y,W=n-m\),这个式子便可写作:\(kW+lz=S\),于是用 exgcd
求出答案
让我们回到原问题。
因为 \(k_j=k_{min}+\frac{l}{gcd(W,l)} \times{t}\),所以 \(k_{min}=k_j \bmod \frac{l}{gcd(W,l)}\) 这是因为 exgcd
的性质
而因为这个 \(k\) 是建立在 \(\rm exgcd\) 得出的方程上的,方程右边是 \(gcd(W,l)\) 而不是 \(S\)。所以最后我们需要将结果 \(\times{ \frac{S}{gcd(W,l)} }\) 。
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
LL exgcd(LL a, LL b, LL &x, LL &y)
{
if (!b)
{
x = 1, y = 0;
return a;
}
LL d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main()
{
LL a, b, m, n, L;
cin >> a >> b >> m >> n >> L;
LL x, y;
LL d = exgcd(m - n, L, x, y);
if ((b - a) % d) puts("Impossible");
else
{
x *= (b - a) / d;
LL t = abs(L / d);
cout << (x % t + t) % t << endl;
}
return 0;
}
P4777 【模板】扩展中国剩余定理(EXCRT)
设有一组两两互质的正整数 \(m_1, m_2, \dots, m_n\)(即 \(\gcd(m_i, m_j) = 1\) 对任意 \(i \neq j\)),以及一组整数 \(a_1, a_2, \dots, a_n\)。中国剩余定理指出,下面的同余方程组在模 \(M = m_1 \times m_2 \times \cdots \times m_n\) 下有唯一解:
- 解的存在性与唯一性
- 存在性:存在解 \(x\) 满足所有同余方程。
- 唯一性:解在模 \(M\) 下唯一,即所有解可表示为 \(x + kM\)(\(k \in \mathbb{Z}\))。
- 解为:$$ x = \sum_{i=1}^n a_i M_i t_i \mod M $$,
证明:
- 因为 \(M_i=M/m_i\),是除 \(m_i\) 之外所有模数的倍数,所以 \(∀k≠i,a_iM_it_i≡0(\mod m_k)\) 。
- 又因为 \(M_it_i≡1(\mod m_i)\) ,所以 \(a_iM_it_i≡a_i(\mod m_i)\) 。
- 综上所述,将公式带入原方程组即能够满足所有的同余式。
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 10;
int n;
int A[N], B[N];
LL exgcd(LL a, LL b, LL &x, LL &y) // 扩展欧几里得算法, 求x, y,使得ax + by = gcd(a, b)
{
if (!b)
{
x = 1; y = 0;
return a;
}
LL d = exgcd(b, a % b, y, x);
y -= (a / b) * x;
return d;
}
int main(){
scanf("%d", &n);
LL M = 1;
for (int i = 0; i < n; i ++ ){
scanf("%d%d", &A[i], &B[i]);
M *= A[i];
}
LL res = 0;
for (int i = 0; i < n; i ++ ){
LL Mi = M / A[i];
LL ti, x;
exgcd(Mi, A[i], ti, x);
res = (res + (__int128)B[i] * Mi * ti) % M;
// B[i] * Mi * ti可能会超出long long范围,所以需要转化成__int128
}
cout << (res % M + M) % M << endl;
return 0;
}