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\),即:

\[\left\{ \begin{aligned} x+u\le v \\ x+u\le w \\ x+v\le w \\ u+v\le w \\ \end{aligned} \right. \]

枚举 \(u\),将不等式变一下:

\[\left\{ \begin{aligned} v\ge x+u \\ w\ge x+u \\ v\le w-x \\ v\le w-u \\ \end{aligned} \right. \]

把这些不等式合成一个式子,\(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\) 的情况:

\[\begin{aligned} {} & \frac{6\times 7}2+\frac{4\times 6}2+\frac{2\times 3}2+\frac{0\times 1}2 & \\ = & 3\times 7+2\times 5+1\times 3+0\times 1 & \\ = & 4\times(1+3+5+7)-(1\times 7+2\times 5+3\times 3+4\times 1) & \\ = & 4\times 4^2-[(1+3+5+7)+(1+3+5)+(1+3)+(1)] & \\ = & 4^3-(4^2+3^2+2^2+1^2) & \\ = & 4^3-\frac{4\times(4+1)\times(2\times 4+1)}{6} & \\ \end{aligned} \]

这可以推广到 \(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

不会。

posted @ 2024-10-16 22:48  liruixiong0101  阅读(8)  评论(0编辑  收藏  举报