2024-10-14 模拟赛总结
\(10+100+100+0=210\),第一题最后 30min 想到思路,没打完 QAQ。
A - 通配符 / char
题意:
给定 \(m\) 个字符串 \(t_1,t_2,\cdots,t_m\),定义一个仅含小写英文字母和 *
的字符串 \(r\) 的美丽度是将所有的 *
替换成任意小写英文字母组成的字符串(可以为空串),令替换后的字符串为 \(q\),美丽度为 \(q=t_1\vee q=t_2\vee\cdots\vee q=t_m\) 的方案数。
给定字符串 \(s\),求 \(s\) 的所有子串的美丽度之和。
思路:
首先有一个显然的结论是可以将所有 \(t_i\) 去重后对于每一个 \(t_i\) 计算方案数然后求和,因为去重后每一个 \(t_i\) 都互不相同那么用 *
替换字符串使字符串相同的方案数也不会算重。
现在考虑对于每一个 \(t\) 如何算出方案数,考虑 dp,设 \(f_{i,j}\) 为 \(t\) 的前 \(j\) 位(连续)匹配 \(s\) 的前 \(i\) 位的方案数。
- 如果当前这一位不为
*
那么如果 \(s_i=t_j\),那么 \(f_{i,j}=f_{i-1,j-1}\)(有点绕口)。 - 如果当前这一位为
*
那么可以将*
替换成 \(t\) 的一段子串(右端点为 \(j\)),那么就是 \(f_{i,j}=\sum_{k=0}^{j} f_{i-1,k}\),这可以用前缀和优化。
考虑边界情况显然有 \(f_{0,0}=1\),\(f_{i,0}=1+f_{i-1,0}\times[s[i]=*]\),\(1\) 是因为什么都不干,\(s[i]=*\) 那么可以匹配空串,否则不能继续匹配。
代码:
#include <bits/stdc++.h>
using namespace std;
const int kMaxN = 5e4 + 5, kMaxM = 1005, kM = 998244853;
int n, m, ans, f[kMaxN][kMaxM], g[kMaxN][kMaxM];
string s, t[kMaxM];
int main() {
freopen("char.in", "r", stdin);
freopen("char.out", "w", stdout);
cin >> n >> m >> s, s = ' ' + s;
for (int i = 1; i <= m; i++) {
cin >> t[i];
}
sort(t + 1, t + 1 + m), m = unique(t + 1, t + 1 + m) - t - 1;
for (int k = 1, l; k <= m; k++) {
l = t[k].size(), t[k] = ' ' + t[k], fill(f[0], f[0] + 1 + l, 0), fill(g[0], g[0] + 1 + l, f[0][0] = 1);
for (int i = 1; i <= n; i++) {
fill(f[i], f[i] + 1 + l, 0), g[i][0] = f[i][0] = 1 + f[i - 1][0] * (s[i] == '*');
for (int j = 1; j <= l; j++) {
s[i] == t[k][j] && (f[i][j] = (f[i][j] + f[i - 1][j - 1]) % kM);
s[i] == '*' && (f[i][j] = (f[i][j] + g[i - 1][j]) % kM);
g[i][j] = (g[i][j - 1] + f[i][j]) % kM;
}
}
for (int i = 1; i <= n; i++) {
ans = (ans + f[i][l]) % kM;
}
}
cout << ans;
return 0;
}
B - 树 / tree
题意:
一棵 \(n\) 个节点的树,每个节点是黑色或白色,若黑色点的集合为 \(S\),白色点的集合为 \(T\),定义这颗树的权值为 \(\sum_{u\in S}\sum_{v\in T} dis(u,v)\),\(dis(u,v)\) 为树上 \(u,v\) 两点的简单路径中边权最大值。
给定一颗 \(n\) 个节点的树,你至多将一个点的颜色转换,求这棵树的最大权值。
思路:
暴力枚举改变哪个点的颜色,然后就变成了并查集板题。
代码:
#include <bits/stdc++.h>
using namespace std;
const int kMaxN = 3005;
int n, fa[kMaxN], siz[2][kMaxN];
bool a[kMaxN];
long long ans;
struct E {
int u, v, w;
} e[kMaxN];
int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); }
long long W(long long ret = 0) {
for (int i = 1; i <= n; i++) {
fa[i] = i, siz[a[i]][i] = 1, siz[a[i] ^ 1][i] = 0;
}
for (int i = 1, u, v, w; i < n; i++) {
u = find(e[i].u), v = find(e[i].v), w = e[i].w;
ret += 1LL * w * (1LL * siz[0][u] * siz[1][v] + 1LL * siz[1][u] * siz[0][v]);
fa[v] = u, siz[0][u] += siz[0][v], siz[1][u] += siz[1][v];
}
return ret;
}
int main() {
freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 1; i < n; i++) {
cin >> e[i].u >> e[i].v >> e[i].w;
}
sort(e + 1, e + n, [](E i, E j) { return i.w < j.w; });
ans = W();
for (int i = 1; i <= n; i++) {
a[i] ^= 1, ans = max(ans, W()), a[i] ^= 1;
}
cout << ans;
return 0;
}
C - 木棍 / wooden
题意:
给定 \(x\),生成三个数 \(x\lt u,v,w\le 4\cdot x\),求 \(x,u,v,w\) 其中三个数当作三角形的三条边可以组成三角形的数量。
思路:
考虑反面,再令 \(x\lt u\le v\le w\le 4\cdot x\),即:
枚举 \(u\),将不等式变一下:
把这些不等式合成一个式子,\(x+u\lt v\le w-u\le 4\cdot x-u\),那么已知 \(x,u\) 方案数即为 \(\dfrac{(3x-2u+1)(3x-2u+2)}{2}\),那么我们就会了 \(O(x)\) 的做法。
考虑这个式子如何转化,我们来看一下 \(x=7\) 的情况:
这可以推广到 \(x\) 为奇数的所有情况,令 \(n=\lceil\dfrac x2\rceil\),那么不合法的方案数即为 \(n^3-\dfrac{n(n+1)(2n+1)}{6}\)。
那么 \(x\) 为偶数呢,其实就是在奇数的基础上加上 \(1+3+5+\cdots\),在答案的基础上加上 \(n^2\) 即可。
代码:
#include <bits/stdc++.h>
using namespace std;
const int kM = 1e9 + 7;
long long t;
__int128 x, n, ans;
int main() {
freopen("wooden.in", "r", stdin);
freopen("wooden.out", "w", stdout);
cin >> t, n = (t + 1) / 2 % kM, x = t % kM;
ans = 3 * x % kM * (3 * x - 1) % kM * (3 * x - 2) % kM * 166666668 % kM;
ans = (ans - n % kM * n % kM * n % kM + n % kM * (n + 1) % kM * (2 * n % kM + 1) % kM * 166666668 % kM + kM) % kM;
t % 2 == 0 && (ans = (ans - n % kM * n % kM + kM) % kM);
cout << (int)ans;
return 0;
}
D - 群星 / star
不会。