20260704 期末考试?总结
题目不难啊,为什么没 AK 呢?
C 罚时总结:题目说了时从前到后排序,第二种情况不要反转。
I 罚时总结:呃?存代码用的。
A - ORXOR
tag:DFS,二进制。
首先二进制枚举断点,然后统计答案。
#include <bits/stdc++.h>
using namespace std;
#define AKCoder
#ifdef AKCoder
template<class t,class u>ostream& operator<<(ostream& os,const pair<t,u>& p){return os<<"("<<p.first<<","<<p.second<<")";}
#define debug(x) cout<<#x<<" = "<<(x)<<'\n'
#else
#define debug(x);
#endif
#define ll long long
#define ull unsigned long long
#define db double
#define all(x) (x).begin(), (x).end()
#define inf (1 << 30)
#define lnf (1LL << 60)
typedef pair<int, int> PII;
constexpr int N = 20 + 7;
constexpr int P = 998244353;
int n, a[N];
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
int ans = inf;
for (int S = 0; S < (1 << n); S++) {
int o = 0, x = 0;
for (int i = 1; i <= n; i++) {
if (S & (1 << (i - 1))) {
x ^= o;
o = 0;
}
o |= a[i];
}
x ^= o;
ans = min(ans, x);
}
printf("%d\n", ans);
return 0;
}
B - Typical Stairs
tag:DP,加法原理
如果没有坏台阶的限制,就是一个简单的 Fibonacci 数列。
然后如果有限制,不访问就好了。
#include <bits/stdc++.h>
using namespace std;
// #define AKCoder
#ifdef AKCoder
template<class t,class u>ostream& operator<<(ostream& os,const pair<t,u>& p){return os<<"("<<p.first<<","<<p.second<<")";}
#define debug(x) cout<<#x<<" = "<<(x)<<'\n'
#else
#define debug(x);
#endif
#define ll long long
#define ull unsigned long long
#define db double
#define all(x) (x).begin(), (x).end()
#define inf (1 << 30)
#define lnf (1LL << 60)
typedef pair<int, int> PII;
constexpr int N = 1e5 + 7;
constexpr int P = 1e9 + 7;
int n, m, a[N];
bool b[N];
ll dp[N];
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++) scanf("%d", &a[i]), b[a[i]] = true;
dp[0] = 1, dp[1] = !b[1];
for (int i = 2; i <= n; i++) {
if (b[i]) continue;
debug(i);
dp[i] = (dp[i - 1] + dp[i - 2]) % P;
}
printf("%lld\n", dp[n]);
return 0;
}
C - Tenzing and Books
tag:贪心,模拟,二进制
直接模拟这个过程,判断能不能选就好了。
#include <bits/stdc++.h>
using namespace std;
#define AKCoder
#ifdef AKCoder
template<class t,class u>ostream& operator<<(ostream& os,const pair<t,u>& p){return os<<"("<<p.first<<","<<p.second<<")";}
#define debug(x) cout<<#x<<" = "<<(x)<<'\n'
#else
#define debug(x);
#endif
#define ll long long
#define ull unsigned long long
#define db double
#define all(x) (x).begin(), (x).end()
#define inf (1 << 30)
#define lnf (1LL << 60)
typedef pair<int, int> PII;
constexpr int N = 1e5 + 7;
constexpr int P = 998244353;
int n, x;
vector<int> a[4];
void solve() {
scanf("%d%d", &n, &x);
for (int i = 1; i <= 3; i++) {
a[i].resize(n);
for (auto &j : a[i]) scanf("%d", &j);
reverse(all(a[i]));
}
int u = 0;
while (!a[1].empty() || !a[2].empty() || !a[3].empty()) {
bool ok = false;
for (int i = 1; i <= 3; i++) {
if (!a[i].empty()) {
int bk = a[i].back(), zs = bk | u;
if ((zs | x) == x) {
u = zs;
a[i].pop_back();
ok = true;
}
}
}
if (!ok) break;
}
puts(u == x ? "Yes" : "No");
}
int main() {
int oT_To = 1;
scanf("%d", &oT_To);
while (oT_To--) solve();
return 0;
}
D - 01迷宫
tag:DFS,
tarjan。
我们发现,一个联通块的点总是可以互相到达的,然后记录连通块就没了。
#include <bits/stdc++.h>
using namespace std;
// #define AKCoder
#ifdef AKCoder
template<class t,class u>ostream& operator<<(ostream& os,const pair<t,u>& p){return os<<"("<<p.first<<","<<p.second<<")";}
#define debug(x) cout<<#x<<" = "<<(x)<<'\n'
#else
#define debug(x);
#endif
#define ll long long
#define ull unsigned long long
#define db double
#define all(x) (x).begin(), (x).end()
#define inf (1 << 30)
#define lnf (1LL << 60)
typedef pair<int, int> PII;
constexpr int N = 1000 + 7;
constexpr int P = 998244353;
constexpr int dx[] = {-1, 0, 0, 1};
constexpr int dy[] = {0, -1, 1, 0};
int n, m;
char s[N][N];
bool vis[N][N];
int bel[N][N], nums[N * N];
int siz, p;
char nxt(char ch) {
return (ch == '1') ? '0' : '1';
}
inline void dfs(int x, int y) {
if (vis[x][y]) return;
vis[x][y] = true;
bel[x][y] = p;
siz++;
for (int i = 0; i < 4; i++) {
int xx = x + dx[i];
int yy = y + dy[i];
if (xx < 1 || xx > n || yy < 1 || yy > n) continue;
debug(xx);
if (nxt(s[x][y]) == s[xx][yy])
dfs(xx, yy);
}
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%s", s[i] + 1);
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (!vis[i][j]) {
siz = 0, ++p;
dfs(i, j);
nums[p] = siz;
}
}
}
while (m--) {
int u, v;
scanf("%d%d", &u, &v);
printf("%d\n", nums[bel[u][v]]);
}
return 0;
}
E - Military Problem
tag:dfs 序,DFS。
我们发现子树的 DFN 是连续的,然后没了。
#include <bits/stdc++.h>
using namespace std;
#define AKCoder
#ifdef AKCoder
template<class t,class u>ostream& operator<<(ostream& os,const pair<t,u>& p){return os<<"("<<p.first<<","<<p.second<<")";}
#define debug(x) cout<<#x<<" = "<<(x)<<'\n'
#else
#define debug(x);
#endif
#define ll long long
#define ull unsigned long long
#define db double
#define all(x) (x).begin(), (x).end()
#define inf (1 << 30)
#define lnf (1LL << 60)
typedef pair<int, int> PII;
constexpr int N = 2e5 + 7;
constexpr int P = 998244353;
int n, q;
vector<int> adj[N];
int dfn[N], id[N], r[N], idx;
inline void dfs(int u, int from) {
dfn[u] = ++idx;
id[idx] = u;
for (auto v : adj[u]) {
if (v == from) continue;
dfs(v, u);
}
r[u] = idx;
}
int main() {
scanf("%d%d", &n, &q);
for (int i = 2; i <= n; i++) {
int f;
scanf("%d", &f);
adj[f].push_back(i);
adj[i].push_back(f);
}
for (int i = 1; i <= n; i++) sort(all(adj[i]));
dfs(1, 0);
while (q--) {
int u, k;
scanf("%d%d", &u, &k);
int lid = dfn[u];
if (lid + k - 1 > r[u]) puts("-1");
else printf("%d\n", id[lid + k - 1]);
}
return 0;
}
F - Transportation
tag:分类讨论,MST。
分情况讨论。
- 啥都不建
- 只建机场
- 只建港口
- 都建
分别建图,然后跑 MST,建议封装。
#include <bits/stdc++.h>
using namespace std;
#define AKCoder
#ifdef AKCoder
template<class t,class u>ostream& operator<<(ostream& os,const pair<t,u>& p){return os<<"("<<p.first<<","<<p.second<<")";}
#define debug(x) cout<<#x<<" = "<<(x)<<'\n'
#else
#define debug(x);
#endif
#define ll long long
#define ull unsigned long long
#define db double
#define all(x) (x).begin(), (x).end()
#define inf (1 << 30)
#define lnf (1LL << 60)
typedef pair<int, int> PII;
constexpr int N = 2e5 + 7;
constexpr int P = 998244353;
int n, m, x[N], y[N], fa[N];
vector<array<int, 3>> vec, vc;
int find(int x) {
if (fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
bool unite(int x, int y) {
x = find(x), y = find(y);
if (x == y) return false;
fa[y] = x;
return true;
}
ll kruskal(int maxn) {
for (int i = 1; i <= maxn; i++) fa[i] = i;
sort(all(vc));
ll ans = 0;
for (auto [w, u, v] : vc) {
if (unite(u, v)) {
ans += w;
}
}
for (int i = 1; i <= n; i++) if (find(1) != find(i)) return 0;
return ans;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &x[i]);
for (int i = 1; i <= n; i++) scanf("%d", &y[i]);
for (int i = 1; i <= m; i++) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
vec.push_back({w, u, v});
}
ll ans = lnf, mst = 0;
// only build road
vc = vec;
mst = kruskal(n);
if (mst)
ans = min(ans, mst);
// only build airport
vc = vec;
for (int i = 1; i <= n; i++) vc.push_back({x[i], i, n + 1});
mst = kruskal(n + 1);
if (mst)
ans = min(ans, mst);
// only build gangkou
vc = vec;
for (int i = 1; i <= n; i++) vc.push_back({y[i], i, n + 2});
mst = kruskal(n + 2);
if (mst)
ans = min(ans, mst);
// airport & gangkou
vc = vec;
for (int i = 1; i <= n; i++) vc.push_back({x[i], i, n + 1});
for (int i = 1; i <= n; i++) vc.push_back({y[i], i, n + 2});
mst = kruskal(n + 2);
if (mst)
ans = min(ans, mst);
printf("%lld\n", ans);
return 0;
}
PS:港口的英文是什么啊!
G - Choosing Capital for Treeland
tag:树形DP(换根)
首先跑 DFS,然后直接反转一次即可。
#include <bits/stdc++.h>
using namespace std;
// #define AKCoder
#ifdef AKCoder
template<class t,class u>ostream& operator<<(ostream& os,const pair<t,u>& p){return os<<"("<<p.first<<","<<p.second<<")";}
#define debug(x) cout<<#x<<" = "<<(x)<<'\n'
#else
#define debug(x);
#endif
#define ll long long
#define ull unsigned long long
#define db double
#define all(x) (x).begin(), (x).end()
#define inf (1 << 30)
#define lnf (1LL << 60)
typedef pair<int, int> PII;
constexpr int N = 2e5 + 7;
constexpr int P = 998244353;
int n;
ll f[N], g[N], siz[N], k[N], l[N];
vector<int> adj[N];
map<array<int, 2>, bool> edge;
inline void dfs1(int u, int from) {
siz[u] = 1;
for (auto v : adj[u]) {
if (v == from) continue;
dfs1(v, u);
f[u] += f[v] + !edge.count({u, v});
siz[u] += siz[v];
}
}
inline void dfs2(int u, int from) {
debug(u);
for (auto v : adj[u]) {
if (v == from) continue;
g[v] += g[u] + (edge.count({v, u}) ? -1 : 1);
dfs2(v, u);
}
}
int main() {
scanf("%d", &n);
for (int i = 1; i < n; i++) {
int u, v;
scanf("%d%d", &u, &v);
adj[u].push_back(v);
adj[v].push_back(u);
edge[{u, v}] = true;
}
dfs1(1, 0);
g[1] = f[1];
dfs2(1, 0);
for (int i = 1; i <= n; i++) debug(f[i]);
for (int i = 1; i <= n; i++) debug(k[i]);
for (int i = 1; i <= n; i++) debug(l[i]);
for (int i = 1; i <= n; i++) debug(g[i]);
int minv = *min_element(g + 1, g + n + 1);
printf("%d\n", minv);
for (int i = 1; i <= n; i++) if (g[i] == minv) printf("%d ", i);
return 0;
}
H - Fox And Jumping
tag:裴蜀定理,EXGCD?,DP,Dijkstra。
如果 \(0\) 能到 \(1\),\(1\) 就一定能到 \(2\),以此类推。
所以,题目就转化成了不定方程问题,充要条件是 \(\gcd = 1\)。
然后就用 Dijkstra 求解即可。
#include <bits/stdc++.h>
using namespace std;
// #define AKCoder
#ifdef AKCoder
template<class t,class u>ostream& operator<<(ostream& os,const pair<t,u>& p){return os<<"("<<p.first<<","<<p.second<<")";}
#define debug(x) cout<<#x<<" = "<<(x)<<'\n'
#else
#define debug(x);
#endif
#define ll long long
#define ull unsigned long long
#define db double
#define all(x) (x).begin(), (x).end()
#define inf (1 << 30)
#define lnf (1LL << 60)
typedef pair<int, int> PII;
constexpr int N = 300 + 7;
constexpr int P = 998244353;
int n, l[N], c[N];
map<int, ll> dis, vis;
void dijkstra(int s) {
priority_queue<PII, vector<PII>, greater<PII>> q;
dis[s] = 0;
q.push({0, s});
while (!q.empty()) {
int u = q.top().second;
q.pop();
if (vis.count(u)) continue;
vis[u] = true;
for (int j = 1; j <= n; j++) {
int w = c[j], v = gcd(u, l[j]);
debug(w);
if (!dis.count(v)) dis[v] = lnf;
if (dis[u] + w < dis[v]) {
dis[v] = dis[u] + w;
q.push({dis[v], v});
}
}
}
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &l[i]);
for (int i = 1; i <= n; i++) scanf("%d", &c[i]);
dijkstra(0);
debug(dis[1]);
if (!dis.count(1)) {
puts("-1");
return 0;
}
printf("%lld\n", dis[1]);
return 0;
}
I - Reducing Delivery Cost
艹,没调出来;想到了思路,结果没调试出来,QAQ,替换边的思想应该很显然吧,循环的 sum 没清空,痛失 rk 4,debug 能力要加强啊。
tag:Dijkstra,替换边思想。
首先先全源最短路,然后枚举边,记录答案即可。
#include <bits/stdc++.h>
using namespace std;
// #define AKCoder
#ifdef AKCoder
template<class t,class u>ostream& operator<<(ostream& os,const pair<t,u>& p){return os<<"("<<p.first<<","<<p.second<<")";}
#define debug(x) cout<<#x<<" = "<<(x)<<'\n'
#else
#define debug(x);
#endif
#define ll long long
#define ull unsigned long long
#define db double
#define all(x) (x).begin(), (x).end()
#define inf (1 << 30)
#define lnf (1LL << 60)
typedef pair<ll, int> PII;
constexpr int N = 1000 + 7;
constexpr int P = 998244353;
int n, m, k;
vector<PII> adj[N];
ll dis[N], d[N][N];
bool vis[N];
void dijkstra(int s) {
priority_queue<PII, vector<PII>, greater<PII>> q;
memset(dis, 0x3f, sizeof(dis));
memset(vis, false, sizeof(vis));
dis[s] = 0;
q.push({0, s});
while (!q.empty()) {
int u = q.top().second;
q.pop();
if (vis[u]) continue;
vis[u] = true;
for (auto [v, w] : adj[u]) {
if (dis[u] + w < dis[v]) {
dis[v] = dis[u] + w;
q.push({dis[v], v});
}
}
}
}
int main() {
scanf("%d%d%d", &n, &m, &k);
for (int i = 1; i <= m; i++) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
adj[u].push_back({v, w});
adj[v].push_back({u, w});
}
for (int i = 1; i <= n; i++) {
dijkstra(i);
for (int j = 1; j <= n; j++) d[i][j] = dis[j];
}
vector<array<int, 2>> q;
for (int i = 1; i <= k; i++) {
array<int, 2> qx;
scanf("%d%d", &qx[0], &qx[1]);
q.push_back(qx);
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
debug(make_pair(i, j));
debug(d[i][j]);
}
}
ll ans = lnf;
for (int i = 1; i <= n; i++) {
for (auto [v, w] : adj[i]) {
ll sum = 0;
debug(make_pair(i, v));
for (auto [qx, qy] : q) {
ll qans = min({d[qx][qy], d[qx][i] + d[v][qy], d[qx][v] + d[i][qy]});
sum += qans;
}
debug(sum);
ans = min(ans, sum);
}
}
printf("%lld\n", ans);
return 0;
}
J - Kaavi and Magic Spell
又没调出来,记忆化是什么?好吃吗?下次我写记忆化我是 数据删除。
tag:区间 DP
首先先补齐位置,比如用 \(114514917831415926\) 填充。
然后设 \(dp_i,_j\) 为 \(t_i \sim t_j\) 匹配 \(s_1 \sim s_{j-i+1}\) 的方案数。
然后可以从两个端点转移,比较可以写个 check。
然后答案就是后面任意选。
#include <bits/stdc++.h>
using namespace std;
#define AKCoder
#ifdef AKCoder
template<class t,class u>ostream& operator<<(ostream& os,const pair<t,u>& p){return os<<"("<<p.first<<","<<p.second<<")";}
#define debug(x) cout<<#x<<" = "<<(x)<<'\n'
#else
#define debug(x);
#endif
#define ll long long
#define ull unsigned long long
#define db double
#define all(x) (x).begin(), (x).end()
#define inf (1 << 30)
#define lnf (1LL << 60)
typedef pair<int, int> PII;
constexpr int N = 3000 + 7;
constexpr int P = 998244353;
const char *str = "114514917831415926";
constexpr int strsize = 18;
char S[N], T[N];
ll dp[N][N];
int main() {
scanf("%s", S + 1);
scanf("%s", T + 1);
int n = strlen(S + 1), m = strlen(T + 1);
for (int i = m + 1; i <= n; i++) T[i] = str[i % strsize];
auto check = [&](char a, char b) -> char {
if (isdigit(b)) return true;
return a == b;
};
for (int i = 1; i <= n; i++) if (check(S[1], T[i])) dp[i][i] = 2;
for (int d = 1; d <= n; d++) {
for (int i = 1; i + d - 1 <= n; i++) {
int j = i + d - 1;
if (check(S[d], T[i])) {
dp[i][j] = (dp[i][j] + dp[i + 1][j]) % P;
}
if (check(S[d], T[j])) {
dp[i][j] = (dp[i][j] + dp[i][j - 1]) % P;
}
}
}
ll ans = 0;
for (int i = m; i <= n; i++) {
ans = (ans + dp[1][i]) % P;
}
printf("%lld\n", ans);
return 0;
}
总结
debug 爽了啊。
下次代码写好一点 QAQ。

浙公网安备 33010602011771号