文章目录
3-Congruences
题意 有 m m m 组同余方程组 x p i ≡ q i ( m o d n ) , i ∈ [ 1 , m ] x^{p_i} \equiv q_i\pmod n,i\in[1,m] xpi≡qi(modn),i∈[1,m], n = ∏ i = 1 m p i n=\prod_{i=1}^m p_i n=∏i=1mpi,其中 p i p_i pi 两两互质,求所有满足条件的 x x x
题解 若有 x p ≡ q ( m o d n ) x^p\equiv q\pmod n xp≡q(modn) 即 x p − q ≡ 0 ( m o d n ) x^p-q\equiv0\pmod n xp−q≡0(modn),则 n ∣ x p − q n|x^p-q n∣xp−q,
而 p ∣ n p|n p∣n,则 p ∣ x p − q p|x^p-q p∣xp−q,从而 x p ≡ q ( m o d p ) x^p\equiv q \pmod p xp≡q(modp)
根据费马小定理 x p − 1 ≡ 1 ( m o d p ) x^{p-1}\equiv 1\pmod p xp−1≡1(modp) 则 x p ≡ x ( m o d p ) x^p\equiv x\pmod p xp≡x(modp),则 x ≡ q ( m o d p ) x\equiv q\pmod p x≡q(modp)
转化为了线性同余方程组,接下来直接利用 C R T CRT CRT 求解即可
上述推理过程可以简化为 x p ≡ q ( m o d n ) ⇒ x p ≡ q ( m o d p ) ⇔ x ≡ q ( m o d p ) ( ∗ 式 ) x^p\equiv q\pmod n\Rightarrow x^p\equiv q \pmod p \Leftrightarrow x\equiv q\pmod p(*式) xp≡q(modn)⇒xp≡q(modp)⇔x≡q(modp)(∗式)
在有解的情况下,求出 r r r,并特判 r = 0 r=0 r=0 的情况,即可得到最小正整数解。
( ∗ 式 ) (*式) (∗式) 第一步两边不互为充要条件,所以 C R T CRT CRT 求出的解不一定是原方程的解,需要验证解是否满足原方程。由于原方程对 n n n 取模,因此只需验证 r r r 一个数即可
#include <iostream>
#include <vector>
#define FastIO cin.tie(nullptr) -> sync_with_stdio(false);
#define Cases int __; cin >> __; for (int _ = 1; _ <= __; _ ++)
using namespace std;
#define lll __int128
typedef long long ll;
inline ll qpow(lll a, ll b, ll p) {
lll res = 1;
while (b) {
if (b & 1) (res *= a) %= p;
(a *= a) %= p;
b >>= 1;
}
return res;
}
inline ll inv(ll x, ll p) { return qpow(x % p, p - 2, p); }
ll CRT(vector<ll> b, vector<ll> m, ll n) {
ll res = 0;
for (int i = 0; i < b.size(); i ++) {
ll r = n / m[i];
res += (lll)r * b[i] * inv(r, m[i]) % n;
}
return res % n;
}
ll solve() {
int m; cin >> m;
vector<ll> a, b, c;
ll n = 1;
for (int i = 0; i < m; i ++) {
ll x, y; cin >> x >> y;
a.push_back(x);
b.push_back(y % x);
c.push_back(y);
n *= x;
}
ll res = CRT(b, a, n);
for (int i = 0; i < m; i ++)
if (qpow(res, a[i], n) != c[i])
return -1;
if (!res) return n;
return res;
}
int main() {
FastIO
Cases
cout << solve() << endl;
return 0;
}
5-0 vs 1
题意 长度为 n n n 的 01 01 01 串, z e r o zero zero 只能取 0 0 0, o n e one one 只能取 1 1 1,每次只能从序列的左右两端取,不能取则对方获胜,若全部取完则平局输出 − 1 -1 −1
题解 若当前轮到 z e r o zero zero:(轮到 o n e one one 同理)
- 若左右两端都是 1 1 1,则对方即 o n e one one 获胜,输出 1 1 1
- 若左右两端不一致,则选择 0 0 0 的那端,接下来轮到 1 1 1
- 若左右两端都是 0 0 0,则此时 z e r o zero zero 掌握了主动权,因为无论 z e r o zero zero 选择哪边接下来 o n e one one 只能跟着选择同一边,同时易发现,能决定胜负的方式只有一端有两个 0 0 0 来使得对方落败,
- 则贪心选择哪侧离下一个 0 0 0 更近(可以作预处理)使得自己获得主动权, e . g . 01...00 e.g.~01...00 e.g. 01...00 中选择右端的 0 0 0, 010...110 010...110 010...110 选择左端的 1 1 1
#include <iostream>
#include <queue>
#include <bitset>
using namespace std;
#define FastIO cin.tie(nullptr) -> sync_with_stdio(false);
#define Cases int __; cin >> __; for (int _ = 1; _ <= __; _ ++)
#define endl '\n'
const int N = 1e5 + 2;
int l[2][N], r[2][N], last[2];
// l/r 存放 i 左/右边第一个 0/1 的下标
bitset<N> b;
int solve() {
int n; cin >> n >> b;
deque<int> dq;
for (int i = 0; i < n; i ++) {
bool t = b[i]; // 当前类型
last[t] = l[t][last[t]] = i;
}
for (int i = n - 1; i >= 0; i --) {
bool t = b[i];
last[t] = r[t][last[t]] = i;
}
for (int i = 0; i < n; i ++) dq.push_back(i);
bool t = 0; // 当前轮到谁
while (!dq.empty()) {
int left = dq.front(), right = dq.back();
if (b[left] == t && b[right] == t)
if (l[t][left] - left <= right - r[t][right]) dq.pop_front();
else dq.pop_back();
else if (b[left] != b[right])
if (b[left] == t) dq.pop_front();
else dq.pop_back();
else return !t;
t = !t;
}
return -1;
}
int main() {
FastIO
Cases
cout << solve() << endl;
return 0;
}
7-Solubility
题意 每次给定两种液体相融,液体相融具有结合律,最后问 k k k 种液体是否能全部相融
Tag 并查集
#include <iostream>
using namespace std;
#define FastIO cin.tie(nullptr) -> sync_with_stdio(false);
#define Cases int __; cin >> __; for (int _ = 1; _ <= __; _ ++)
#define endl '\n'
const string yes = "YES";
const string no = "NO";
const int N = 1e5 + 2;
int n, m, k, p[N];
inline int find(int x) { return x == p[x] ? x : p[x] = find(p[x]); }
string solve() {
cin >> n >> m;
for (int i = 1; i <= n; i ++) p[i] = i;
while (m --) {
int a, b; cin >> a >> b;
p[find(a)] = p[find(b)];
}
cin >> k;
k --;
int last; cin >> last;
bool flag = true;
while (k --) {
int x; cin >> x;
if (p[find(x)] != p[find(last)]) flag = false;
last = x;
}
if (flag) return yes;
return no;
}
int main() {
FastIO
Cases
cout << solve() << endl;
return 0;
}
10-Rikka with Square Numbers
题意 将 a a a 变成 b b b,每次可以加或减去一个完全平方数
题解 记 n = ∣ a − b ∣ n=|a-b| n=∣a−b∣,则只要计算加减完全平方数得到 n n n 的最少步骤
i i i | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
---|---|---|---|---|---|---|---|---|---|---|---|
i 2 i^2 i2 | 1 | 4 | 9 | 16 | 25 | 36 | 49 | 64 | 81 | 100 | 121 |
i 2 − ( i − 1 ) 2 i^2-(i-1)^2 i2−(i−1)2 | N/A | 3 | 5 | 7 | 9 | 11 | 13 | 15 | 17 | 19 | 21 |
i 2 − ( i − 2 ) 2 i^2-(i-2)^2 i2−(i−2)2 | N/A | N/A | 8 | 12 | 16 | 20 | 24 | 28 | 32 | 36 | 40 |
当 n n n 为完全平方数时,只需要一步操作
因为通过 i 2 − ( i − 1 ) 2 i^2-(i-1)^2 i2−(i−1)2 即两部操作得到所有大于 1 1 1 的奇数,减去 1 1 1 即可得到所有偶数,最坏也只需要 3 3 3 次
其次考虑 i 2 − ( i − 2 ) 2 i^2-(i-2)^2 i2−(i−2)2 即两部操作得到所有大于 4 4 4 的 4 4 4 的倍数
#include <iostream>
#include <cmath>
using namespace std;
#define FastIO cin.tie(nullptr) -> sync_with_stdio(false);
#define Cases int __; cin >> __; for (int _ = 1; _ <= __; _ ++)
#define endl '\n'
#define check(x) pow((int)sqrt(x), 2) == x
int solve() {
int a, b; cin >> a >> b;
int n = abs(a - b);
if (check(n)) return 1;
for (int i = 1; i * i <= n / 2; i ++)
if (check(n - i * i))
return 2;
if ((n & 1) || n % 4 == 0) return 2;
return 3;
}
int main() {
FastIO
Cases
cout << solve() << endl;
return 0;
}