模板题,直接用exgcd
就行了,上一场的青蛙的约会搞懂了,自然就会了。
#include
#define int long long
using namespace std;
int exgcd(int a, int b, int& x, int& y) {
if (b == 0) {
x = 1, y = 0;
return a;
}
int nx, ny;
int res = exgcd(b, a % b, nx, ny);
x = ny;
y = nx - a / b * ny;
return res;
}
int T, a, b, x, y, d;
signed main() {
cin >> T;
while (T--) {
cin >> a >> b;
d = exgcd(a, b, x, y);
if (d != 1) {
cout << -1 << "\n";
} else {
cout << (x % b + b) % b << "\n";
}
}
return 0;
}
方法1:线性求逆元,详细方式推导可以参考oi-wiki https://oi-wiki.org/math/number-theory/inverse/#_5
#include
#define int long long
using namespace std;
const int N = 3e6 + 10;
int n, p, inv[N];
signed main() {
cin >> n >> p;
inv[1] = 1;
for (int i = 2; i <= n; i++)
inv[i] = (p - (p / i) * inv[p % i] % p) % p;
for (int i = 1; i <= n; i++)
cout << inv[i] << "\n";
return 0;
}
方法2:因为 n n n的数据范围比较大,所以不能用费马小定理+快速幂硬求,考虑如何减少用快速幂求逆元的次数。
对于数组 a 1 , a 2 , a 3 , a 4 a_1,a_2,a_3,a_4 a1,a2,a3,a4,若要求 a 2 a_2 a2的逆元,只需要求 a 2 − 1 = ( a 1 a 2 a 3 a 4 ) − 1 ⋅ ( a 1 a 3 a 4 ) a_2^{-1}=(a_1a_2a_3a_4)^{-1}\cdot (a_1a_3a_4) a2−1=(a1a2a3a4)−1⋅(a1a3a4),记录一个前缀和一个后缀就行了。(当时写的时候并不会线性求逆元)
#include
#define int long long
using namespace std;
const int N = 3e6 + 10;
int n, p, pre[N], suf[N];
int k = 1;
int power(int a, int b) {
int res = 1;
while (b) {
if (b & 1) res = (__int128)res * a % p;
a = (__int128)a * a % p;
b >>= 1;
}
return res;
}
int inv(int x) { return power(x, p - 2); }
signed main() {
cin >> n >> p;
for (int i = 1; i <= n; i++) k = (__int128)k * i % p;
k = inv(k);
pre[0] = suf[n + 1] = 1;
for (int i = 1; i <= n; i++) pre[i] = (__int128)pre[i - 1] * i % p;
for (int i = n; i >= 1; i--) suf[i] = (__int128)suf[i + 1] * i % p;
for (int i = 1; i <= n; i++) {
int inv_i = (__int128)pre[i - 1] * suf[i + 1] % p * k % p;
cout << inv_i << "\n;
}
return 0;
}
由上图可知,可以将求和式 ∑ i = 0 n s i a n − i b i \sum_{i=0}^ns_ia^{n-i}b^i ∑i=0nsian−ibi分成 c n t = ( n + 1 ) / k cnt=(n+1)/k cnt=(n+1)/k个小段,每一段内的求和值为 t [ i ] = t [ i − 1 ] × a − k b k , t [ 1 ] = ∑ i = 0 k − 1 s i a n − i b i t[i]=t[i-1]\times a^{-k}b^k,t[1]=\sum_{i=0}^{k-1}s_ia^{n-i}b^i t[i]=t[i−1]×a−kbk,t[1]=∑i=0k−1sian−ibi,可以用等比数列前 n n n项和的公式求出,公比为 a − k b k a^{-k}b^k a−kbk。
注意
:当公比为1时,不能用等比数列前 n n n项和来计算,需要特判。
对于最后剩下的长度不足 k k k的一段,可以直接暴力求解。
#include
#define int long long
using namespace std;
const int N = 1e5 + 10;
const int mod = 1e9 + 9;
int n, a, b, k, pre[N];
int res, w;
char s[N];
int power(int a, int b) {
int res = 1;
while (b) {
if (b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
int inv(int x) { return power(x, mod - 2); }
signed main() {
scanf("%lld%lld%lld%lld", &n, &a, &b, &k);
scanf("%s", s);
w = power(b, k) * inv(power(a, k)) % mod;
for (int i = 0; i < k; i++) {
if (i != 0) pre[i] = pre[i - 1];
if (s[i] == '+') {
pre[i] = (pre[i] + power(a, n - i) * power(b, i) % mod) % mod;
} else {
pre[i] = (pre[i] - power(a, n - i) * power(b, i) % mod + mod) % mod;
}
}
int cnt = (n + 1) / k;
if (w == 1) { // 特判分母为0的情况
res = pre[k - 1] * cnt % mod;
} else { // 等比数列前cnt项和
res = pre[k - 1] * ((power(w, cnt) - 1 + mod) % mod) % mod *
inv((w - 1 + mod) % mod) % mod;
}
res = (res + pre[n - (n + 1) / k * k] * power(w, (n + 1) / k) % mod) % mod;
printf("%lld", res);
return 0;
}
直接套excrt
的板子。
#include
#define int long long
using namespace std;
const int N = 1e5 + 10;
int exgcd(int a, int b, int &x, int &y) {
if (!b) {
x = 1, y = 0;
return a;
}
int nx, ny;
int d = exgcd(b, a % b, nx, ny);
x = ny;
y = nx - a / b * ny;
return d;
}
int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); }
int lcm(int a, int b) { return a / gcd(a, b) * b; }
int excrt(int k, int *a, int *r) {
int M = r[1], ans = a[1];
for (int i = 2; i <= k; i++) {
int x0, y0;
int c = a[i] - ans;
int g = exgcd(M, r[i], x0, y0);
if (c % g != 0) return -1;
x0 = (__int128)x0 * (c / g) % (r[i] / g);
ans = x0 * M + ans;
M = lcm(M, r[i]);
ans = (ans % M + M) % M;
}
return ans;
}
int n, a[N], b[N];
signed main() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i] >> b[i];
int res = excrt(n, b, a);
cout << res;
return 0;
}
当三重峰值出现时,一定满足 x = p + 23 t 1 = e + 28 t 2 = i + 33 t 3 x=p+23t_1=e+28t_2=i+33t_3 x=p+23t1=e+28t2=i+33t3,可以列出同余方程为
{ x ≡ p ( m o d 23 ) x ≡ e ( m o d 28 ) x ≡ i ( m o d 33 ) \left\{\begin{matrix} x\equiv p(\mod 23) \\ x\equiv e(\mod 28) \\ x\equiv i(\mod 33) \end{matrix}\right. ⎩⎨⎧x≡p(mod23)x≡e(mod28)x≡i(mod33)
直接套excrt
,求出通解。
注意题目要求的是从日期d到下一个三重峰的天数,要求出特解。
#include
#define int long long
using namespace std;
const int N = 1e5 + 10;
int exgcd(int a, int b, int &x, int &y) {
if (!b) {
x = 1, y = 0;
return a;
}
int nx, ny;
int d = exgcd(b, a % b, nx, ny);
x = ny;
y = nx - a / b * ny;
return d;
}
int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); }
int lcm(int a, int b) { return a / gcd(a, b) * b; }
pair<int, int> excrt(int k, int *a, int *r) {
int M = r[1], ans = a[1];
for (int i = 2; i <= k; i++) {
int x0, y0;
int c = a[i] - ans;
int g = exgcd(M, r[i], x0, y0);
if (c % g != 0) return {-1, -1};
x0 = (__int128)x0 * (c / g) % (r[i] / g);
ans = x0 * M + ans;
M = lcm(M, r[i]);
ans = (ans % M + M) % M;
}
return {ans, M};
}
int T, p, e, i, d;
int a[10], b[10];
signed main() {
cin >> T;
while (T--) {
cin >> p >> e >> i >> d;
a[1] = p, b[1] = 23;
a[2] = e, b[2] = 28;
a[3] = i, b[3] = 33;
pair<int, int> w = excrt(3, a, b);
w.first -= d;
w.first = (w.first % w.second + w.second) % w.second;
if (w.first == 0) w.first = w.second;
cout << w.first << "\n";
}
return 0;
}
欧拉降幂裸题。
所有 n n n位 b b b进制的数有 b n b^n bn个,含前导 0 0 0的数有 b n − 1 b^{n-1} bn−1个,总共需要记录的数有 b n − b n − 1 = ( b − 1 ) b n − 1 b^n-b^{n-1}=(b-1)b^{n-1} bn−bn−1=(b−1)bn−1个,直接对 c c c取模。
当取模结果为 0 0 0时,最后一页记录的数字是 c c c个,需要特判一下。
#include
#define int long long
using namespace std;
const int N = 1e5 + 10;
int power(int a, int b, int mod) {
int res = 1;
while (b) {
if (b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
int f(int n) { // 求n的欧拉函数
int res = n;
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) {
res = res / i * (i - 1);
while (n % i == 0) n /= i;
}
}
if (n > 1) res = res / n * (n - 1);
return res;
}
int b, n, c;
string str_b, str_n;
signed main() {
cin >> str_b >> str_n >> c;
for (int i = 0; i < str_b.size(); i++) b = (b * 10 + (str_b[i] - '0')) % c;
int phi_c = f(c);
bool k = false;
for (int i = 0; i < str_n.size(); i++) {
n = n * 10 + (str_n[i] - '0');
if (n - 1 >= phi_c) n %= phi_c, k = true;
}
n--;
if (k) n += phi_c;
int res = (b - 1 + c) % c * power(b, n, c) % c;
if (!res) res = c;
cout << res;
return 0;
}
m → φ ( m ) → φ ( φ ( m ) ) → . . . → 1 m\rightarrow \varphi(m) \rightarrow \varphi(\varphi(m)) \rightarrow...\rightarrow1 m→φ(m)→φ(φ(m))→...→1 ,这个步骤最多会进行 log m \log m logm次。
对于每次的查询区间 [ l , r ] [l,r] [l,r],最多计算 log m \log m logm次,总的复杂度就是 O ( q log m ) O(q\log m) O(qlogm)。
计算幂的时候要用到欧拉降幂。
#include
#define int long long
using namespace std;
const int N = 1e5 + 10;
int n, m, w[N], q;
int phi_m[N], total;
bool k;
int power(int a, int b, int mod) {
int res = 1;
while (b) {
if (b & 1) {
res = res * a;
if (res >= mod) {
k = true;
res %= mod;
}
}
a = a * a;
if (a >= mod) {
k = true;
a %= mod;
}
b >>= 1;
}
return res;
}
int f(int n) {
int res = n;
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) {
res = res / i * (i - 1);
while (n % i == 0) n /= i;
}
}
if (n > 1) res = res / n * (n - 1);
return res;
}
void init() {
int tmp = m;
total = 0;
phi_m[++total] = tmp;
while (tmp != 1) {
tmp = f(tmp);
phi_m[++total] = tmp;
}
}
void solve(int l, int r) {
r = min(r, l - 1 + total);
int res = 1;
for (int i = r; i >= l; i--) {
k = false;
res = power(w[i], res, phi_m[i - l + 1]);
if (k) res += phi_m[i - l + 1];
}
cout << res % phi_m[1] << "\n";
}
signed main() {
cin >> n >> m;
init(); // 预处理欧拉函数的值
for (int i = 1; i <= n; i++) cin >> w[i];
cin >> q;
while (q--) {
int l, r;
cin >> l >> r;
solve(l, r);
}
return 0;
}
若选中的起始位置为 ( i , j ) (i,j) (i,j),则:
{ g c d ( i , j ) ) = a 1 g c d ( i , j + 1 ) ) = a 2 g c d ( i , j + 2 ) ) = a 3 ⇔ { j ≡ 0 ( m o d a 1 ) j ≡ − 1 ( m o d a 2 ) j ≡ − 2 ( m o d a 3 ) \begin{matrix} \left\{\begin{matrix} gcd(i,j)) &=& a_1 \\ gcd(i,j+1)) &=& a_2 \\ gcd(i,j+2)) &=& a_3 \end{matrix}\right. & \Leftrightarrow & \left\{\begin{matrix} j &\equiv& 0(\mod a_1) \\ j &\equiv& -1 (\mod a_2) \\ j &\equiv& -2 (\mod a_3) \end{matrix}\right. \end{matrix} ⎩⎨⎧gcd(i,j))gcd(i,j+1))gcd(i,j+2))===a1a2a3⇔⎩⎨⎧jjj≡≡≡0(moda1)−1(moda2)−2(moda3)
上式可以通过excrt
求得 j j j的通解: j = j 0 + M ⋅ x , M = l c m ( a 1 , a 2 . . . a k ) j=j_0+M\cdot x,\ M=lcm(a_1,a_2...a_k) j=j0+M⋅x, M=lcm(a1,a2...ak),且 i i i是 M M M的倍数, i = M + M ⋅ y i=M+M\cdot y i=M+M⋅y。
若 g = g c d ( M , j 0 ) g=gcd(M,j_0) g=gcd(M,j0), g ′ = g c d ( M + M ⋅ y , j 0 + M ⋅ x ) g'=gcd(M+M\cdot y,j_0+M\cdot x) g′=gcd(M+M⋅y,j0+M⋅x),可以看出 g ∣ g ′ g|g' g∣g′。
只需要判断 j = j 0 , i = M j=j_0,i=M j=j0,i=M是否成立就行了,因为 g c d ( i , j + t ) gcd(i,j+t) gcd(i,j+t)有可能是 a t + 1 a_{t+1} at+1的倍数,这样条件就不成立。
#include
#define int long long
using namespace std;
const int N = 1e4 + 10;
int exgcd(int a, int b, int &x, int &y) {
if (!b) {
x = 1, y = 0;
return a;
}
int nx, ny;
int d = exgcd(b, a % b, nx, ny);
x = ny;
y = nx - a / b * ny;
return d;
}
int lcm(int a, int b) { return a / __gcd(a, b) * b; }
pair<int, int> excrt(int k, int *a, int *r) {
int M = r[1], ans = a[1];
for (int i = 2; i <= k; i++) {
int x0, y0;
int c = a[i] - ans;
int g = exgcd(M, r[i], x0, y0);
if (c % g != 0) return {-1, -1};
x0 = (__int128)x0 * (c / g) % (r[i] / g);
ans = x0 * M + ans;
M = lcm(M, r[i]);
ans = (ans % M + M) % M;
}
return {ans, M};
}
int n, m, k, a[N];
int b[N];
signed main() {
cin >> n >> m >> k;
for (int i = 1; i <= k; i++) cin >> a[i], b[i] = -(i - 1);
pair<int, int> x = excrt(k, b, a);
if (x.first == 0) x.first = x.second;
if (x.first > m - k + 1 || x.second > n || x.first == -1) {
cout << "NO";
} else {
for (int i = 0; i < k; i++)
if (__gcd(x.first + i, x.second) != a[i + 1]) {
cout << "NO";
return 0;
}
cout << "YES";
}
return 0;
}