[练习记录] 《算法竞赛进阶指南》打卡活动
89. a^b
题目大意
给 \(a,b,p\) 求 \(a^b \mod p\)。
思路
可以直接快速幂。当模数 \(p\) 为 \(1\) 的时候特判一下。
代码
ll a, b, mod;
ll qpow(ll a, ll b) {
ll res = 1;
while (b) {
if (b & 1) res = res * a % mod;
a = a * a % mod, b >>= 1;
}
return res;
}
int main() {
a = read(), b = read(), mod = read();
if (mod == 1) printf("0\n");
else printf("%lld\n", qpow(a, b));
return 0;
}
AcWing 90. 64位整数乘法
题目大意
给 \(a,b,p\) 求 \(a \times b \mod p\)。
思路1
好像是想让我写龟速乘,但是我直接 __int128
莽过去了()。
代码1
__int128 a, b, mod;
void prt(__int128 x) {
if (x > 9) prt(x / 10);
putchar(x % 10 + '0');
}
int main() {
a = read(), b = read(), mod = read();
__int128 ans = a * b % mod;
prt(ans);
return 0;
}
思路2
龟速乘,长得和快速幂很像。
代码2
ll a, b, mod;
ll mul(ll a, ll b) {
ll res = 0;
while (b) {
if (b & 1) res = (res + a) % mod;
a = (a << 1) % mod, b >>= 1;
}
return res;
}
int main() {
a = read(), b = read(), mod = read();
printf("%lld\n", mul(a, b));
return 0;
}
91. 最短Hamilton路径
题目大意
给一张 \(n\) 个点的带权无向完全图,求从 \(1\) 到 \(n\) 恰好经过所有点 \(1\) 次的路径的最短长度。
思路
注意到 \(n\leq 20\),考虑状压dp。
设状态 \(f_{i,s}\) 表示经过了状态为 \(s\) 的点,目前在点 \(i\) 处的路径的最短长度。
那么转移很显然 \(f_{j,s|(1<<j)}=\min(f_{j,s|(1<<j)},f_{i,s}+e_{i,j})\),其中 \(i\) 在 \(s\) 状态中被访问过,\(j\) 没有。
代码
const int N = 21, INF = 0x3f3f3f3f;
int n, a[N][N];
int f[N][1 << N];
int main() {
n = read();
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
a[i][j] = read();
memset(f, 0x3f, sizeof(f));
f[1][1] = 0;
for (int s = 0; s < 1 << n; s++)
for (int i = 1; i <= n; i++) {
if ((s & (1 << (i - 1))) == 0) continue;
for (int j = 1; j <= n; j++) {
if (s & (1 << (j - 1)) == 1) continue;
f[j][s | (1 << (j - 1))] = min(f[j][s | (1 << (j - 1))], f[i][s] + a[i][j]);
}
}
printf("%d\n", f[n][(1 << n) - 1]);
return 0;
}
998. 起床困难综合症
题目大意
给出一系列 \(AND,OR,XOR\) 操作,求 \(0\) ~ \(m\) 内的数经过操作后得到的结果的最大值。
思路
按位填。
代码
const int N = 100010;
int n, m, op[N], t[N], ans;
int calc(int x, int w) {
for (int i = 0; i < n; i++) {
if (op[i] == 1) x |= (t[i] >> w) & 1;
if (op[i] == 2) x ^= (t[i] >> w) & 1;
if (op[i] == 3) x &= (t[i] >> w) & 1;
}
return x;
}
int main() {
n = read(), m = read();
for (int i = 0; i < n; i++) {
string s; cin >> s;
if (s == "OR") op[i] = 1;
if (s == "XOR") op[i] = 2;
if (s == "AND") op[i] = 3;
t[i] = read();
}
for (int i = 29; i >= 0; i--) {
if (1 << i > m) {
ans |= calc(0, i) << i;
continue;
}
int x = calc(0, i), y = calc(1, i);
if (x >= y) ans += x << i;
else ans += y << i, m -= 1 << i;
}
printf("%d\n", ans);
return 0;
}
92. 递归实现指数型枚举
题目大意
给出 \(n\),输出所有 \(1\) ~ \(n\) 的升序选取方案。
思路
直接dfs。
代码
const int N = 21;
int n;
int ans[N], cnt;
void dfs(int x) {
if (x > n) {
for (int i = 1; i <= cnt; i++) printf("%d ", ans[i]);
puts("");
return ;
}
ans[++cnt] = x;
dfs(x + 1);
cnt--;
dfs(x + 1);
}
int main() {
n = read();
dfs(1);
return 0;
}
93. 递归实现组合型枚举
题目大意
输出从 \(1\) ~ \(n\) 中选取 \(m\) 个的所有方案。
思路
按题目名称递归求。
代码
const int N = 25;
int n, m;
int ans[N], cnt;
void dfs(int x) {
if (cnt == m) {
for (int i = 1; i <= cnt; i++) printf("%d ", ans[i]);
puts("");
return ;
}
if (x > n) return ;
ans[++cnt] = x;
dfs(x + 1);
cnt--;
dfs(x + 1);
}
int main() {
n = read(), m = read();
dfs(1);
return 0;
}
94. 递归实现排列型枚举
题目大意
输出 \(1\) ~ \(n\) 的全排列。
思路
暴力dfs。
代码
const int N = 11;
int n;
int ans[N];
bool used[N];
void dfs(int x) {
if (x > n) {
for (int i = 1; i <= n; i++) printf("%d ", ans[i]);
puts("");
return ;
}
for (int i = 1; i <= n; i++) {
if (used[i]) continue;
used[i] = 1;
ans[x] = i;
dfs(x + 1);
used[i] = 0;
}
}
int main() {
n = read();
dfs(1);
return 0;
}