26.5~26.6 新生活前奏
26.5.17 2026 ICPC-Wuhan inv solved 4 Penalty 972 rank 86 Cu
26.5.20 vp 2026 SNCPC solved 7 Penalty 583
26.5.22 vp 2025 CCPC-Nanchang inv solved 7 Penalty 726
26.5.24 2026 HBCPC solved 7 Penalty 648 rank 47 Ag
26.5.29 vp 2025 CCPC-Zhengzhou Regional Solved 3 Penalty 662
半步之遥了,综上所述感觉是吃罚时吃的,遗憾凭什么只有我承担。欠的有点多,也不知道学什么新东西,先补两天题。此外强烈建议以后应该把 vp 调到睡醒午觉。
2026 HBCPC M.拯救猫猫
遗忘的技能点。答案只注意可达性,考虑可撤销并查集。枚举 ban 掉的狗,每次操作相当于使该狗视线两侧的若干连通块联通,检查起点和终点是否在一个连通块里,然后再撤销合并操作即可。显然每个点至多被遍历一次,复杂度 \(\mathcal O(nm)\),我的写法令我写起来有点恶心。
点击查看代码
#include<bits/stdc++.h>
#define mp make_pair
#define eb emplace_back
#define fi first
#define se second
using namespace std;
static const int MAX = 2010;
int dx[] = {0, 0, 1, -1};
int dy[] = {1, -1, 0, 0};
int top, fa[MAX * MAX], siz[MAX * MAX];
struct dsu
{
int x, fa, siz;
dsu(int X = 0, int Fa = 0, int Siz = 0) {x = X, fa = Fa, siz = Siz;}
}stk[MAX * MAX << 2];
inline int find(int x) {while(x != fa[x]) x = fa[x]; return x;}
inline void merge(int x, int y)
{
x = find(x), y = find(y);
if(x == y) return;
if(siz[x] < siz[y]) swap(x, y);
stk[++top] = dsu(x, fa[x], siz[x]), stk[++top] = dsu(y, fa[y], siz[y]);
siz[x] += siz[y], fa[y] = x;
}
inline void back(int lst) {while(top > lst) fa[stk[top].x] = stk[top].fa, siz[stk[top].x] = stk[top].siz, --top;}
inline bool check(int x, int y) {return find(x) == find(y);}
int n, m, Top, v[MAX][MAX], s[MAX][MAX];
int sx, sy, ex, ey;
char c[MAX][MAX];
queue<pair<int, int> > q;
inline int id(int x, int y) {return (x - 1) * m + y;}
inline void solve()
{
cin >> n >> m, top = 0;
vector<pair<int, int> > dog;
for(int i = 1; i <= n; ++i) for(int j = 1; j <= m; ++j) cin >> c[i][j], v[i][j] = s[i][j] = 0;
for(int i = 1; i <= n; ++i) for(int j = 1; j <= m; ++j)
{
fa[id(i, j)] = id(i, j), siz[id(i, j)] = 1;
if(c[i][j] == 'S') sx = i, sy = j, c[i][j] = '.';
if(c[i][j] == 'E') ex = i, ey = j, c[i][j] = '.';
if(c[i][j] == 'U')
{
for(int x = i - 1; x >= 1; --x) if(c[x][j] == '.') ++s[x][j]; else break;
dog.eb(mp(i, j));
}
if(c[i][j] == 'D')
{
for(int x = i + 1; x <= n; ++x) if(c[x][j] == '.') ++s[x][j]; else break;
dog.eb(mp(i, j));
}
if(c[i][j] == 'L')
{
for(int y = j - 1; y >= 1; --y) if(c[i][y] == '.') ++s[i][y]; else break;
dog.eb(mp(i, j));
}
if(c[i][j] == 'R')
{
for(int y = j + 1; y <= m; ++y) if(c[i][y] == '.') ++s[i][y]; else break;
dog.eb(mp(i, j));
}
}
for(int i = 1; i <= n; ++i) for(int j = 1; j <= m; ++j) if(c[i][j] == '.' && !v[i][j] && !s[i][j])
{
q.emplace(mp(i, j)), v[i][j] = 1;
while(!q.empty())
{
auto [x, y] = q.front(); q.pop();
for(int k = 0; k < 4; ++k)
{
int tx = x + dx[k], ty = y + dy[k];
if(tx >= 1 && tx <= n && ty >= 1 && ty <= m && !v[tx][ty] && c[tx][ty] == '.' && !s[tx][ty])
{
merge(id(i, j), id(tx, ty));
v[tx][ty] = 1;
q.emplace(mp(tx, ty));
}
}
}
}
Top = top;
for(int k = 0; k < (int)dog.size(); ++k)
{
int dogx = dog[k].fi, dogy = dog[k].se;
if(c[dogx][dogy] == 'U')
{
for(int i = dogx - 1; i >= 1; --i)
{
if(c[i][dogy] == '.')
{
if(s[i][dogy] != 1) continue;
if(i > 1 && c[i - 1][dogy] == '.' && s[i - 1][dogy] <= 1) merge(id(i, dogy), id(i - 1, dogy));
if(dogy > 1 && c[i][dogy - 1] == '.' && s[i][dogy - 1] <= 1) merge(id(i, dogy), id(i, dogy - 1));
if(dogy < m && c[i][dogy + 1] == '.' && s[i][dogy + 1] <= 1) merge(id(i, dogy), id(i, dogy + 1));
}
else break;
}
if(check(id(sx, sy), id(ex, ey))) return cout << dogx << " " << dogy << '\n', void();
back(Top);
}
if(c[dogx][dogy] == 'D')
{
for(int i = dogx + 1; i <= n; ++i)
{
if(c[i][dogy] == '.')
{
if(s[i][dogy] != 1) continue;
if(i < n && c[i + 1][dogy] == '.' && s[i + 1][dogy] <= 1) merge(id(i, dogy), id(i + 1, dogy));
if(dogy > 1 && c[i][dogy - 1] == '.' && s[i][dogy - 1] <= 1) merge(id(i, dogy), id(i, dogy - 1));
if(dogy < m && c[i][dogy + 1] == '.' && s[i][dogy + 1] <= 1) merge(id(i, dogy), id(i, dogy + 1));
}
else break;
}
if(check(id(sx, sy), id(ex, ey))) return cout << dogx << " " << dogy << '\n', void();
back(Top);
}
if(c[dogx][dogy] == 'L')
{
for(int i = dogy - 1; i >= 1; --i)
{
if(c[dogx][i] == '.')
{
if(s[dogx][i] != 1) continue;
if(i > 1 && c[dogx][i - 1] == '.' && s[dogx][i - 1] <= 1) merge(id(dogx, i), id(dogx, i - 1));
if(dogx > 1 && c[dogx - 1][i] == '.' && s[dogx - 1][i] <= 1) merge(id(dogx, i), id(dogx - 1, i));
if(dogx < n && c[dogx + 1][i] == '.' && s[dogx + 1][i] <= 1) merge(id(dogx, i), id(dogx + 1, i));
}
else break;
}
if(check(id(sx, sy), id(ex, ey))) return cout << dogx << " " << dogy << '\n', void();
back(Top);
}
if(c[dogx][dogy] == 'R')
{
for(int i = dogy + 1; i <= m; ++i)
{
if(c[dogx][i] == '.')
{
if(s[dogx][i] != 1) continue;
if(i < m && c[dogx][i + 1] == '.' && s[dogx][i + 1] <= 1) merge(id(dogx, i), id(dogx, i + 1));
if(dogx > 1 && c[dogx - 1][i] == '.' && s[dogx - 1][i] <= 1) merge(id(dogx, i), id(dogx - 1, i));
if(dogx < n && c[dogx + 1][i] == '.' && s[dogx + 1][i] <= 1) merge(id(dogx, i), id(dogx + 1, i));
}
else break;
}
if(check(id(sx, sy), id(ex, ey))) return cout << dogx << " " << dogy << '\n', void();
back(Top);
}
}
cout << "-1 -1" << '\n';
}
signed main()
{
ios::sync_with_stdio(false), cin.tie(nullptr);
int T = 1; cin >> T;
while(T--) solve();
return (0 - 0);
}
2026 HBCPC G.建设高铁
等价于 kruskal 重构树上每个叶子有一个权值,修改操作为改叶子的权值,询问操作为选取前 \(k_i\) 大的叶子求他们的 LCA。首先有结论:若干个叶子的 LCA 等价于其中 dfs 序编号最大的叶子和最小的叶子的 LCA。考虑对权值做值域线段树,节点上存 dfs 序信息,每次查询即在线段树上二分,然后直接倍增求 LCA。比较难搞的是权值有重复,还要考虑相同权值优先小编号的问题,可以重新带编号赋值做二次离散化去重。复杂度 \(\mathcal O(q \log (n + m))\),赛时没考虑好去重的问题导致没出。
点击查看代码
#include<bits/stdc++.h>
#define mp make_pair
#define eb emplace_back
#define fi first
#define se second
using namespace std;
static const int MAX = 500010;
static const int INF = INT_MAX;
int n, m, q, d[MAX << 1], cnt;
struct edge{int x, y, z;}t;
vector<edge> E;
vector<int> G[MAX << 2];
struct operation{int op, c, t, k;}a[MAX];
int val[MAX << 1], fa[MAX << 1];
int find(int x) {return x == fa[x] ? x : fa[x] = find(fa[x]);}
int dep[MAX << 2], f[MAX << 2][20], id[MAX << 2], rid[MAX << 2], tot;
void dfs(int now, int father)
{
dep[now] = dep[father] + 1, f[now][0] = father;
id[now] = ++tot, rid[tot] = now;
for(int i = 1; i <= __lg(dep[now]); ++i) f[now][i] = f[f[now][i - 1]][i - 1];
for(auto to:G[now]) if(to != father) dfs(to, now);
}
inline int LCA(int x, int y)
{
if(dep[x] < dep[y]) swap(x, y);
while(dep[x] > dep[y]) x = f[x][__lg(dep[x] - dep[y])];
if(x == y) return x;
for(int i = __lg(dep[x]); ~i; --i) if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
return f[x][0];
}
int N, sum[MAX << 2], maxx[MAX << 2], minx[MAX << 2];
inline void pushup(int i)
{
sum[i] = sum[i << 1] + sum[i << 1 | 1];
maxx[i] = max(maxx[i << 1], maxx[i << 1 | 1]);
minx[i] = min(minx[i << 1], minx[i << 1 | 1]);
}
void build(int i, int l, int r)
{
if(l == r) return minx[i] = INF, maxx[i] = -INF, void();
int mid = (l + r) >> 1;
build(i << 1, l, mid), build(i << 1 | 1, mid + 1, r);
pushup(i);
}
void modify(int i, int l, int r, int x, int k)
{
if(l == r)
{
if(k == 0) sum[i] = 0, minx[i] = INF, maxx[i] = -INF;
else sum[i] = 1, minx[i] = maxx[i] = k;
return;
}
int mid = (l + r) >> 1;
if(x <= mid) modify(i << 1, l, mid, x, k); else modify(i << 1 | 1, mid + 1, r, x, k);
pushup(i);
}
inline void modify(int x, int k) {modify(1, 1, N, x, k);}
pair<int, int> query(int i, int l, int r, int s)
{
if(s == sum[i]) return mp(minx[i], maxx[i]);
int mid = (l + r) >> 1;
if(s <= sum[i << 1 | 1]) return query(i << 1 | 1, mid + 1, r, s);
else
{
pair<int, int> res = query(i << 1, l, mid, s - sum[i << 1 | 1]);
return mp(min(res.fi, minx[i << 1 | 1]), max(res.se, maxx[i << 1 | 1]));
}
}
inline pair<int, int> query(int s) {return query(1, 1, N, s);}
inline void solve()
{
cin >> n >> m >> q;
for(int i = 1; i <= n; ++i) cin >> val[i], d[++cnt] = val[i];
for(int i = 1; i <= (n << 1); ++i) fa[i] = i;
for(int i = 1; i <= m; ++i) cin >> t.x >> t.y >> t.z, E.eb(t);
sort(E.begin(), E.end(), [&](edge a, edge b){return a.z < b.z;});
for(int i = 1; i <= q; ++i)
{
cin >> a[i].op;
if(a[i].op == 1) cin >> a[i].c >> a[i].t, d[++cnt] = a[i].t;
if(a[i].op == 2) cin >> a[i].k;
}
sort(d + 1, d + cnt + 1, [&](int a, int b){return a < b;});
cnt = unique(d + 1, d + cnt + 1) - d - 1;
for(int i = 1; i <= n; ++i) val[i] = lower_bound(d + 1, d + cnt + 1, val[i]) - d;
for(int i = 1; i <= q; ++i) if(a[i].op == 1) a[i].t = lower_bound(d + 1, d + cnt + 1, a[i].t) - d;
cnt = 0;
for(int i = 1; i <= n; ++i) val[i] = val[i] * n - i, d[++cnt] = val[i];
for(int i = 1; i <= q; ++i) if(a[i].op == 1) a[i].t = a[i].t * n - a[i].c, d[++cnt] = a[i].t;
sort(d + 1, d + cnt + 1, [&](int a, int b){return a < b;});
cnt = unique(d + 1, d + cnt + 1) - d - 1;
for(int i = 1; i <= n; ++i) val[i] = lower_bound(d + 1, d + cnt + 1, val[i]) - d;
for(int i = 1; i <= q; ++i) if(a[i].op == 1) a[i].t = lower_bound(d + 1, d + cnt + 1, a[i].t) - d;
N = cnt, cnt = 0;
for(auto [x, y, z]:E)
{
x = find(x), y = find(y);
if(x == y) continue;
++cnt, val[cnt + n] = z;
G[cnt + n].eb(x), fa[x] = cnt + n;
G[cnt + n].eb(y), fa[y] = cnt + n;
if(cnt == n - 1) break;
}
dfs(cnt + n, 0);
build(1, 1, N);
for(int i = 1; i <= n; ++i) modify(val[i], id[i]);
for(int i = 1; i <= q; ++i)
{
pair<int, int> res;
if(a[i].op == 1) modify(val[a[i].c], 0), val[a[i].c] = a[i].t, modify(val[a[i].c], id[a[i].c]);
if(a[i].op == 2) {if(a[i].k == 1) cout << 0 << '\n'; else res = query(a[i].k), cout << val[LCA(rid[res.fi], rid[res.se])] << '\n';}
}
}
signed main()
{
ios::sync_with_stdio(false), cin.tie(nullptr);
int T = 1; //cin >> T;
while(T--) solve();
return (0 - 0);
}
2025 CCPC-Zhengzhou reg I.Dumb Problem II
求 \(k\) 个长度为 \(n\) 的随机排列本质不同的前缀 \(\max\) 数字集合个数。什么时候会数数?
记得到的前缀 \(\max\) 集合为 \(S\),考虑从大到小依次插入 \(x = n, n - 1, ..., 1\)。那么只会有两种情况:
- \(x\) 在 \(S\) 中,那么 \(x\) 必须在比它的所有数的前面,只有一个插入的位置。
- \(x\) 不在 \(S\) 中,此时前面已经插入了 \(n - x\) 个数,\(x\) 可以在除了首位的任意一个位置,那么 \(x\) 有 \(n - x\) 个位置可以插入。
所以 \(S\) 出现的频率为 \(F_S = \prod\limits_{x \notin S} (n - x)\),显然对于随机生成的一个排列,它导出集合为 \(S\) 的概率为 \(P_S = \dfrac{\prod\limits_{x \notin S} (n - x)}{n!}\)。
然后考虑随机选择了 \(k\) 个集合,要求不同集合的期望数。也就是对于所有集合 \(S\),在抽到的 \(k\) 个排列中至少出现一次的概率之和。接下来推柿子:
对于后面这个枚举子集我们考虑一个多项式展开的意义:考虑有若干个数 \(A_i\),对于 \((1 + A_1) (1 + A_2) ... (1 + A_n) = \prod\limits_{i = 1}^{n} (1 + A_i)\) 的完全展开,得到的每一项实质上是 \(n\) 个括号中要么选 \(1\) 要么选 \(A_i\) 相乘的结果,选 \(1\) 等价于 \(i \in S\),选 \(A_i\) 等价于 \(i \notin S\),于是展开后的每一项都对应着唯一一个 \(S \subseteq {1, 2, ..., n}\)。综上所述:
预处理组合数和阶乘逆元,至此做到 \(\mathcal O(nk)\)。
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
static const int MAX = 5010;
static const int mod = 998244353;
int n, k, fac, ans;
int C[MAX][MAX];
inline int qpow(int a, int b)
{
int res = 1;
while(b)
{
if(b & 1) res = res * a % mod;
a = a * a % mod, b >>= 1;
}
return res;
}
inline void solve()
{
cin >> n >> k, fac = 1;
for(int i = 0; i <= k; ++i) C[i][0] = 1;
for(int i = 1; i <= k; ++i) for(int j = 1; j <= i; ++j) C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % mod;
for(int i = 1; i <= n; ++i) fac = (fac * i) % mod;
for(int i = 1; i <= k ;++i)
{
int res = 1;
for(int j = 0; j < n; ++j) res = res * (1 + qpow(j, i)) % mod;
if(i & 1) ans += ((C[k][i] * qpow(qpow(fac, i), mod - 2) % mod) * res) % mod, ans %= mod;
else ans -= ((C[k][i] * qpow(qpow(fac, i), mod - 2) % mod) * res) % mod, ans = (ans % mod + mod) % mod;
}
cout << ans << '\n';
}
signed main()
{
ios::sync_with_stdio(false), cin.tie(nullptr);
int T = 1; //cin >> T;
while(T--) solve();
return (0 - 0);
}
2025 CCPC-Zhengzhou reg J.Subrectangle Count
设 \(x = c_{i,j} = a_{i} \oplus b_{j}\),通过条件可以得到一些柿子:
对于序列 \(b\) 要满足 \(x \oplus (x + 1) = (x + 2) \oplus (x + 3) = b_{j} \oplus b_{j + 1}\),只考虑二进制下后三位:
- \(x = 000_2, x + 1 = 001_2, x + 2 = 010_2, x + 3 = 011_2\),那么 \(x \oplus (x + 1) = 001_2, (x + 2) \oplus (x + 3) = 001_2\),因此 \(x \oplus (x + 1) = (x + 2) \oplus (x + 3)\),符合。
- \(x = 001_2, x + 1 = 010_2, x + 2 = 011_2, x + 3 = 100_2\),那么 \(x \oplus (x + 1) = 011_2, (x + 2) \oplus (x + 3) = 111_2\),因此 \(x \oplus (x + 1) \neq (x + 2) \oplus (x + 3)\),不符合。
- \(x = 010_2, x + 1 = 011_2, x + 2 = 100_2, x + 3 = 101_2\),那么 \(x \oplus (x + 1) = 001_2, (x + 2) \oplus (x + 3) = 001_2\),因此 \(x \oplus (x + 1) = (x + 2) \oplus (x + 3)\),符合。
- \(x = 011_2, x + 1 = 100_2, x + 2 = 101_2, x + 3 = 110_2\),那么 \(x \oplus (x + 1) = 111_2, (x + 2) \oplus (x + 3) = 011_2\),因此 \(x \oplus (x + 1) \neq (x + 2) \oplus (x + 3)\),不符合。
综上所述,\(b_{j} \oplus b_{j + 1}\) 首先必须是偶数(即 \(x\) 为偶数),且容易得出 \(b_{j} \oplus b_{j + 1} = 1\)。接下来考虑序列 \(a\) 的性质,记 \(D = a \oplus a_{i + 1} = x \oplus (x + 2)\),那么不妨令 \(x = 2k\) ,则 \(D = 2k \oplus (2k + 2) = 2(k \oplus (k + 1))\)。有结论 \(x \oplus (x + 1)\) 必须形如 \(2^p - 1\),所以可得出 \(D \equiv 2^p - 2 (\mod 2^p)\),经过计算可得 \(x \equiv 2^{p - 1} - 2 (\mod 2^p)\)。
据此,我们要完成三步:
- 找出所有 \(b_j \oplus b_{j + 1} = 1\) 的 \(j\)。
- 找出所有 \(a_i \oplus a_{i + 1} = 2^p - 2\) 的 \(i\)。
- 找出 \(b_j \mod 2^p = (a_i \mod 2^p) \oplus (2^{p - 1} - 2)\)。
上述过程可以用 trie 实现,复杂度 \(\mathcal O((n + m) \log V)\)。
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
static const int MAX = 200010;
static const int MAXH = 30;
int n, m, ans, a[MAX], b[MAX];
struct Trie
{
int cnt, ch[MAX << 3][2], p[MAX << 3];
inline void insert(int x)
{
int now = 1;
for(int i = 0; i < MAXH; ++i)
{
if(!ch[now][(x >> i) & 1]) ch[now][(x >> i) & 1] = ++cnt;
now = ch[now][(x >> i) & 1];
}
++p[now];
}
inline int query(int x, int t)
{
int now = 1;
for(int i = 0; i < t; ++i)
{
if(!ch[now][(x >> i) & 1]) return 0;
now = ch[now][(x >> i) & 1];
}
return p[now];
}
void dfs(int now)
{
if(ch[now][0]) dfs(ch[now][0]), p[now] += p[ch[now][0]];
if(ch[now][1]) dfs(ch[now][1]), p[now] += p[ch[now][1]];
}
}t[2];
inline void solve()
{
cin >> n >> m, ans = 0;
for(int i = 1; i <= t[0].cnt; ++i) t[0].ch[i][0] = t[0].ch[i][1] = t[0].p[i] = 0;
for(int i = 1; i <= t[1].cnt; ++i) t[1].ch[i][0] = t[1].ch[i][1] = t[1].p[i] = 0;
t[0].cnt = t[1].cnt = 1;
for(int i = 1; i <= n; ++i) cin >> a[i];
for(int i = 1; i <= m; ++i) cin >> b[i];
for(int i = 1; i < m; ++i) if((b[i] ^ b[i + 1]) == 1) t[b[i] & 1].insert((b[i]));
t[0].dfs(1), t[1].dfs(1);
for(int i = 1; i < n; ++i) if(__builtin_popcountll((a[i] ^ a[i + 1]) + 2) == 1)
{
int tmp = __builtin_ctzll((a[i] ^ a[i + 1]) + 2);
ans += t[a[i]&1].query(((1ll << (tmp - 1)) - 2) ^ a[i], tmp);
}
cout << ans << '\n';
}
signed main()
{
ios::sync_with_stdio(false), cin.tie(nullptr);
int T = 1; cin >> T;
while(T--) solve();
return (0 - 0);
}
2025 JXCPC D.挑战图同构
定义最短路为经过的最长路径长,给你两张无向图,判断是否两张图中两两点对的最短路都一样长。考虑做一个模拟最小生成树的过程,每次对于一个权值 \(v\),把 \(D_{(x,y)} \leq v\) 的边全部并查集合并,只要两张图的并查集是完全相同的,那么该定义下的最短路就是完全相同的。判断并查集是否相同可以采用随机异或哈希,初始给并查集上每个点赋一个随机值,合并的时候做异或操作;枚举边权的时候统计一下可能发生变化的点判断即可,复杂度 \(\mathcal O((m_1 + m_2) \log (m_1 + m_2))\)。
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define ins insert
using namespace std;
static const int MAX = 200010;
mt19937_64 engine(time(NULL));
int n, m0, m1, flag;
int p0, p1, cnt, d[MAX << 1];
struct edge{int x, y, z;}t;
vector<edge> E[2];
struct DSU
{
int fa[MAX], val[MAX];
int find(int x) {return x == fa[x] ? x: fa[x] = find(fa[x]);}
inline bool merge(int x, int y)
{
x = find(x), y = find(y);
return x == y ? 0 : (fa[y] = x, val[x] ^= val[y], 1);
}
}F[2];
inline void solve()
{
cin >> n >> m0 >> m1, cnt = flag = 0;
E[0].clear(), E[1].clear(), p0 = 0, p1 = 0;
for(int i = 1; i <= n; ++i) F[0].fa[i] = F[1].fa[i] = i, F[0].val[i] = F[1].val[i] = engine();
for(int i = 1; i <= m0; ++i) cin >> t.x >> t.y >> t.z, E[0].emplace_back(t), d[++cnt] = t.z;
for(int i = 1; i <= m1; ++i) cin >> t.x >> t.y >> t.z, E[1].emplace_back(t), d[++cnt] = t.z;
sort(E[0].begin(), E[0].end(), [&](edge x, edge y){return x.z < y.z;});
sort(E[1].begin(), E[1].end(), [&](edge x, edge y){return x.z < y.z;});
sort(d + 1, d + cnt + 1, [&](int x, int y){return x < y;});
cnt = unique(d + 1, d + cnt + 1) - d - 1;
for(int i = 1; i <= cnt; ++i)
{
set<int> s;
while(p0 < (int)E[0].size() && E[0][p0].z <= d[i])
{
auto [x, y, z] = E[0][p0];
++p0;
if(F[0].merge(x, y)) s.ins(x), s.ins(y);
}
while(p1 < (int)E[1].size() && E[1][p1].z <= d[i])
{
auto [x, y, z] = E[1][p1];
++p1;
if(F[1].merge(x, y)) s.ins(x), s.ins(y);
}
for(auto x:s) flag |= F[0].val[F[0].find(x)] != F[1].val[F[1].find(x)];
}
puts(flag ? "NO" : "YES");
}
signed main()
{
ios::sync_with_stdio(false), cin.tie(nullptr);
int T = 1; cin >> T;
while(T--) solve();
return (0 - 0);
}
2026 ICPC-Shenzhen inv L.Telepathy
显示出思维差异。把 \(S\) 中的数视为左括号,非 \(S\) 中的数视为右括号。对序列做括号匹配,因为 \(|S| < \dfrac{k}{2}\),所以必定存在未匹配的右括号,选择最后一个未匹配的右括号加入 \(S\)。因为最后一个右括号变成了左括号,它的后面必然不可能有右括号了,所以如果对新的序列再做括号匹配,这个新的左括号一定不会被匹配。因此再模拟一遍括号匹配,输出栈底残留的元素即为答案。
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
static const int MAX = 10000010;
string S;
int n, m, x, ans, a[MAX];
inline void solve()
{
cin >> n >> m;
stack<int> stk;
for(int i = 1; i <= n; ++i) a[i] = 0;
for(int i = 1; i <= m + (S == "Bob"); ++i) cin >> x, a[x] = 1;
for(int i = 1; i <= n; ++i) if(a[i]) stk.push(i); else {if(!stk.empty()) stk.pop(); else ans = i;}
if(S == "Alice") {
for(int i = 1; i <= n; ++i) if(a[i]) cout << i << " ";
cout << ans << '\n';
} else {
while(!stk.empty()) ans = stk.top(), stk.pop();
cout << ans << '\n';
}
}
signed main()
{
ios::sync_with_stdio(false), cin.tie(nullptr);
cin >> S;
int T = 1; cin >> T;
while(T--) solve();
return (0 - 0);
}
2026 ICPC-Shenzhen inv F.Astra
考虑定根怎么做。一个点的 sg 值等于先从这个点出发选取一条 \(\leq k\) 的路径,去掉路径上的点后得到若干棵分离的互不影响的子树,把这些子树的 sg 值异或起来;然后对于所有选取路径的情况取 mex。这里可以 dfs(外层做 dp) 套 dfs(内层暴力枚举选取路径)做到 \(\mathcal O(n^2)\)。
考虑对所有点换根怎么做。原来方向的 sg 值是不会改变的,只需要考虑选取从当前点到原定根的路径即可,这部分的影响再做内层 dfs 后异或上就好了。综上总复杂度在 \(\mathcal O(n^2)\),不是很好写。
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define eb emplace_back
using namespace std;
static const int MAX = 2010;
int n, k, x, y, f[MAX], vis[MAX], sg[MAX];
vector<int> G[MAX];
char ans[MAX];
void get(int now, int father, int lenth, int res)
{
res ^= f[now];
if(res <= n) vis[res] = 1;
if(!lenth) return;
for(auto to:G[now]) if(to != father) get(to, now, lenth - 1, res ^ sg[to]);
}
int calc(int now, int father)
{
for(int i = 0; i <= n; ++i) vis[i] = 0;
get(now, father, k - 1, 0);
for(int i = 0; i <= n; ++i) if(!vis[i]) return i;
return n;
}
void dfs1(int now, int father)
{
f[now] = 0;
for(auto to:G[now]) if(to != father) dfs1(to, now), f[now] ^= sg[to];
sg[now] = calc(now, father);
}
void dfs2(int now, int father, int res)
{
ans[now - 1] = '1' - !(res ^ f[now]);
int tmp = sg[now];
for(auto to:G[now]) if(to != father) f[now] ^= sg[to] ^ res, sg[now] = calc(now, to), dfs2(to, now, sg[now]), f[now] ^= sg[to] ^ res;
sg[now] = tmp;
}
inline void solve()
{
cin >> n >> k, ans[n] = '\0';
for(int i = 1; i < n; ++i) cin >> x >> y, G[x].eb(y), G[y].eb(x);
dfs1(1, 0), dfs2(1, 1, 0), cout << ans << '\n';
for(int i = 1; i <= n; ++i) G[i].clear();
}
signed main()
{
ios::sync_with_stdio(false), cin.tie(nullptr);
int T = 1; cin >> T;
while(T--) solve();
return (0 - 0);
}
对于简单的事情总会有莫名其妙的过度自信,如果还不能认识到自己的平庸,将对伟大的渴望收敛于内而不外显,可能这辈子都无法成功。题感觉还是很能练的,最终还是输在不会博弈和数数上。
2026 蓝桥杯国A 魔法前缀
多年前的回旋镖。。。删掉一个前缀等价于在 trie 树上断开一条边。转化题意后,等价于在 trie 树上做 AGC017D。得到 \(sg_u = \oplus_{(u, v) = 1} sg_v + 1\),最后看 \(sg_0\) 是不是 \(0\) 即可。复杂度 \(\mathcal O(\Sigma |S|)\)
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
static const int MAX = 1000010;
int n, cnt, ch[MAX][26], sg[MAX];
string s;
inline void solve()
{
for(int i = 0; i <= cnt; ++i) memset(ch[i], 0, sizeof ch[i]), sg[i] = 0;
cin >> n, cnt = 0;
for(int i = 1; i <= n; ++i)
{
cin >> s;
int now = 0;
for(int j = 0; j < (int)s.size(); ++j)
{
int to = s[j] - 'a';
if(!ch[now][to]) ch[now][to] = ++cnt;
now = ch[now][to];
}
}
function<void(int)> dfs = [&dfs](int now) {for(int i = 0; i < 26; ++i) if(ch[now][i]) dfs(ch[now][i]), sg[now] ^= sg[ch[now][i]] + 1;};
dfs(0);
cout << (sg[0] ? "XiaoLan" : "XiaoQiao") << '\n';
}
signed main()
{
ios::sync_with_stdio(false), cin.tie(nullptr);
int T = 1; cin >> T;
while(T--) solve();
return (0 - 0);
}
2026 蓝桥杯国A 神秘排列
简单数数不会做系列。假设存在一个平衡位置 \(i\),它的数值是 \(x\),左边有 \(k\) 个数比它小,那么左边就会有 \(i - 1 - k\) 个数比它大,因为是排列,所以右侧就会有 \(n - x - (i - 1 - k)\) 个数比它大。我们需要使 \(k = n - x - (i - 1 - k)\),解得 \(i = n - x + 1\)。这意味着一个位置若是平衡位置,其数值必须对应降序排列。只需要枚举平衡位置的数量,钦定一些平衡位置,剩下的位置全部错排就不会重复计算,答案即为 \(\sum_{i = \lceil \frac{n}{2} \rceil}^{n} \binom{n}{i} D_{n - i}\)。预处理错排列数和阶乘,复杂度 \(\mathcal O(n)\)。
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
static const int MAX = 1e7 + 10;
static const int mod = 1e9 + 7;
int n, ans, fac[MAX], inv[MAX], D[MAX];
inline int qpow(int a, int b) {int res = 1; while(b) {if(b & 1) (res *= a) %= mod; (a *= a) %= mod, b >>= 1;} return res;}
inline int C(int N, int M) {return N >= M ? ((inv[M] * inv[N - M] % mod) * fac[N] % mod) : 0;}
inline void solve()
{
cin >> n;
fac[0] = 1;
for(int i = 1; i <= n; ++i) fac[i] = fac[i - 1] * i % mod;
D[0] = 1, D[1] = 0, D[2] = 1;
for(int i = 3; i <= n; ++i) D[i] = (i - 1) * ((D[i - 1] + D[i - 2]) % mod) % mod;
inv[n] = qpow(fac[n], mod - 2);
for(int i = n - 1; ~i; --i) inv[i] = inv[i + 1] * (i + 1) % mod;
for(int i = (n - 1) / 2 + 1; i <= n; ++i) (ans += C(n, i) * D[n - i] % mod) %= mod;
cout << ans << '\n';
}
signed main()
{
ios::sync_with_stdio(false), cin.tie(nullptr);
int T = 1; //cin >> T;
while(T--) solve();
return (0 - 0);
}

浙公网安备 33010602011771号