基本知识
费马小定理
欧拉定理及其推论
欧拉定理
推论
扩战欧几里得算法
【解决线性同余方程,用来求解ax+by=gcd(a,b)】
裴蜀定理【贝组定理】
- 贝祖定理:若a,b是整数,且gcd(a,b)=d,那么对于任意的整数x,y,ax+by=m中的m一定是d的倍数。(特别地,如果a、b是整数,那么一定存在整数x、y使得ax+by=gcd(a,b)。)
- ax+by=c【即ax=c(mod b)】有解当且仅当gcd(a,b)|c,其特解为x=x0(c/gcd(a,b)),y=y0*(c/gcd(a,b))
int exgcd(int a, int b, int &x, int &y) //扩展欧几里得算法
{
if (b == 0) //递归出口
{
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, x, y); //递归
int temp = x;
x = y, y = temp - (a / b) * y; //y=x(old)-(a/b)*y(old)
return d; //d=gcd(a,b)
}
线性同余方程
中国剩余定理
【解决线性同余方程组且模数两两互质,求出的是特解】
自己默认的是形如x=ai (mod bi)这样分布
int crt(int *a, int *b)//中国剩余定理
{
int M = 1, ans = 0;
for (int i = 1; i <= n; i++)
M *= b[i];
for (int i = 1; i <= n; i++)
{
int Mi = M / b[i];
exgcd(Mi, b[i], x, y);//可以看作求逆元
ans += (Mi * x * a[i]) % M;//主要根据公式Σ(ai*Mi*x)
}
return (ans < 0 ? ans += M : ans);
}
扩展中国剩余定理
【解决模数可不互质的线性同余方程组】
ll excrt()
{
ll ans = a[1], lcm = b[1]; //第一个方程的解特判,m是前i个b[i]的lcm
for (ll i = 2; i <= n; i++)
{ //c=a[i]-x即是等式方程右边的;d=gcd(lcm,b[i])
ll c = ((a[i] - ans) % b[i] + b[i]) % b[i], d = exgcd(lcm, b[i], x, y);//求解lcm*t+b[i]*y=gcd(lcm,b[i])
if (c % d != 0)//有解当且仅当d|(a[i]-x)
return -1;
ll bg = b[i] / d;//模数bg
x = fast_mul(x, c / d, bg);//求解lcm*t+b[i]*y=a[i]-x
ans += x * lcm;//更新答案,这里不能用快速乘
lcm *= bg;//lcm为前i个b[i]的lcm,所以新的lcm=(lcm*b[i])/gcd(lcm,b[i])=(lcm*b[i])/d=lcm*bg
}
return (ans % lcm + lcm) % lcm;//最小非负整数解
}
牛客一道小栗子Strange Way to Express Integers
#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef unsigned long long ull;
typedef pair<long long, long long> pll;
const int N = 1e6 + 100;
const int M = 1e7 + 7;
const int inf = 0x3f3f3f3f;
const int eps = 1e-6;
const int mod = 9901;
#define IOS ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
ll n, x, y;
ll a[N], b[N];
ll fast_mul(ll a, ll b, ll mo) //快速乘
{
ll res = 0;
for (; b; b >>= 1)
{
if (b & 1)
res = (res + a) % mo;
a = (a + a) % mo;
}
return res;
}
ll exgcd(ll a, ll b, ll &x, ll &y) //扩展欧几里得
{
if (b == 0)
{
x = 1, y = 0;
return a;
}
ll d = exgcd(b, a % b, x, y);
ll temp = x;
x = y, y = temp - (a / b) * y;
return d;
}
ll excrt() //扩展中国剩余定理
{
ll ans = a[1], lcm = b[1]; //第一个方程的解特判,lcm是前i个b[i]的lcm
for (ll i = 2; i <= n; i++)
{ //c=a[i]-x即是等式方程右边的;d=gcd(lcm,b[i])
ll c = ((a[i] - ans) % b[i] + b[i]) % b[i], d = exgcd(lcm, b[i], x, y); //求解lcm*t+b[i]*y=gcd(lcm,b[i])
if (c % d != 0) //有解当且仅当d|(a[i]-x)
return -1;
ll bg = b[i] / d;
x = fast_mul(x, c / d, bg); //求解lcm*t+b[i]*y=a[i]-x
ans += x * lcm; //更新答案,这里不能用快速乘
lcm *= bg; //lcm为前i个b[i]的lcm,所以新的lcm=(lcm*b[i])/gcd(lcm,b[i])=(lcm*b[i])/d=lcm*bg
}
return (ans % lcm + lcm) % lcm;
}
int main()
{
scanf("%lld", &n);
for (ll i = 1; i <= n; i++)
scanf("%lld%lld", &b[i], &a[i]);
printf("%lld", excrt());
return 0;
}
高次同余方程
大步小步算法
用来解决高次同余方程【前提:a,p互质】
ll bsgs(ll a, ll b, ll p) //大步小步算法【求解a^x=b( mod p),关键思路是将x写成x=i*t-j】
{
map<ll, ll> ha;
ha.clear(), b %= p; //数据的预处理
ll t = (ll)sqrt(p) + 1; //√p向上取整
for (ll j = 0; j < t; j++) //将b*a^j放进hash表
{
ll val = b * fast_pow(a, j, p) % p; //别忘记%p
ha[val] = j;
}
a = fast_pow(a, t, p);
if (a == 0)
return (b == 0 ? 1 : -1);//很重要
for (ll i = 0; i <= t; i++)
{
ll val = fast_pow(a, i, p);
ll j = (ha.find(val) == ha.end() ? -1 : ha[val]);
if (j >= 0 && i * t - j >= 0)
return i * t - j;
}
return -1;
}
洛谷一道小栗子计算器
#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef unsigned long long ull;
typedef pair<long long, long long> pll;
const int N = 1e6 + 100;
const int M = 1e7 + 7;
const int inf = 0x3f3f3f3f;
const int eps = 1e-6;
const int mod = 9901;
#define IOS ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
ll t, k, x, y, z, p, ans;
ll fast_pow(ll a, ll b, ll mo)
{
ll res = 1;
for (; b; b >>= 1)
{
if (b & 1)
res = res * a % mo;
a = a * a % mo;
}
return res % mo;
}
ll exgcd(ll a, ll b, ll &x, ll &y)
{
if (b == 0)
{
x = 1, y = 0;
return a;
}
ll d = exgcd(b, a % b, x, y);
ll temp = x;
x = y, y = temp - (a / b) * y;
return d;
}
ll bsgs(ll a, ll b, ll p) //大步小步算法
{
map<ll, ll> ha;
ha.clear(), b %= p; //数据的预处理
ll t = (ll)sqrt(p) + 1; //√p向上取整
for (ll j = 0; j < t; j++) //将b*a^j放进hash表
{
ll val = b * fast_pow(a, j, p) % p; //别忘记%p
ha[val] = j;
}
a = fast_pow(a, t, p);
if (a == 0)
return (b == 0 ? 1 : -1); //很重要
for (ll i = 0; i <= t; i++)
{
ll val = fast_pow(a, i, p);
ll j = (ha.find(val) == ha.end() ? -1 : ha[val]);
if (j >= 0 && i * t - j >= 0)
return i * t - j;
}
return -1;
}
int main()
{
scanf("%lld%lld", &t, &k);
while (t--)
{
ans = 0;
scanf("%lld%lld%lld", &y, &z, &p);
if (k == 1)
ans = fast_pow(y, z, p);
else if (k == 2)
{
ll d = exgcd(y, p, x, y);
if (z % d) //先判断是否有解【d|c】
ans = -1;
else
{
ans = x * z / d;
ans = (ans % p + p) % p; //最小非负整数
}
}
else if (k == 3)
ans = bsgs(y, z, p);
if (ans == -1)
puts("Orz, I cannot find x!");
else
printf("%d\n", ans);
}
return 0;
}