2021“MINIEYE杯”中国大学生算法设计超级联赛(3) 杭电多校3 题解

sdxjzsq

2021-08-03 15:38:11

Personal

## 目录 | 题号 | 题目 | 知识点 | 完成度 | | ---- | ------------------------------------------------------------ | ------ | ------ | | 1003 | HDU6975 [Forgiving Matching](https://acm.hdu.edu.cn/showproblem.php?pid=6975) | FFT | √ | | 1009 | HDU6981 [Rise in Price](https://acm.hdu.edu.cn/showproblem.php?pid=6981) | 宽搜+剪枝 | √ | | 1010 | HDU6982 [Road Discount](https://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1010&cid=986) | - | - | | 1001 | HDU6973 [Bookshop](https://acm.hdu.edu.cn/showproblem.php?pid=6973) | - | - | | 1012 | HDU6984 [Tree Planting](https://acm.hdu.edu.cn/showproblem.php?pid=6984) | - | - | | 1002 | HDU6974 [Destinations](https://acm.hdu.edu.cn/showproblem.php?pid=6974) | - | - | | 1006 | HDU6978 [New Equipments II](https://acm.hdu.edu.cn/showproblem.php?pid=6978) | - | - | | 1005 | HDU6977 [Kart Race](https://acm.hdu.edu.cn/showproblem.php?pid=6977) | x | x | | 1008 | HDU6980 [Restore Atlantis II](https://acm.hdu.edu.cn/showproblem.php?pid=6980) | x | x | ## 1003 HDU6975 [Forgiving Matching](https://acm.hdu.edu.cn/showproblem.php?pid=6975) ### 题意 已知两个字符串 $s$ 和 $t$,字符集为 $0\sim 9$ 和 $*$,其中 $*$ 可以匹配任意字符,现在要在容错的情况下在 $s$ 中匹配 $t$,问在容错字符数为 $0\cdots \left|t\right|$ 的情况下分别能够匹配到多少次。 ### 思路 在多项式中,结果的第 $i$ 个系数是 $\sum_{j=0}^{i}a_j*b_{i-j}$ ,而 $t$ 去匹配 $s$ 的 $i\sim i+|t|-1$ 位能够匹配上的字符个数是 $\sum_{j=0}^{|t|-1}s_j==t_j$ ,可以发现,如果将 $t$ 逆置,则可以化为 $\sum_{j=0}^{|t|-1}s_j==t_{|t|-1-j}$ ,如果再将逆置后的 $t$ 后面补一些字符集外的字符,则可以变成 $\sum_{j=0}^{i}s_j==t_{i-j}$ ,发现除了比较符号外,和多项式乘法的结果完全一致! 那么如何将比较符号变成乘法呢?观察到题目中字符集只有 $0\sim 9$ 和 $*$ ,因此可以对于每种字符分开计算,每次将 $s$ 和 $t$ 中是该种字符的位置置为 $1$ ,其余位置置为 $0$ ,则对于特定字符来说,$s_j==t_{i-j}\iff s_j\times t_{i-j}$ 。 因此,我们可以对于字符集内每种字符做一遍多项式乘法,则第 $i+|t|-1$ 个系数的结果和便对应 $t$ 去匹配 $s[i,i+|t|-1]$ 能匹配上的字符个数,用 $|t|$ 减去这个个数便是需要容错的字母个数。 ### code ```cpp #include <algorithm> #include <cmath> #include <cstdio> using namespace std; const int maxn = 2e5 + 7, maxm = 6e5; int n, m, T, ans[maxn], num[maxn]; char s[maxn], t[maxn]; const double PI = acos(-1.0); struct Complex { double x, y; Complex(double _x = 0.0, double _y = 0.0) { x = _x, y = _y; } Complex operator-(const Complex &b) const { return Complex(x - b.x, y - b.y); } Complex operator+(const Complex &b) const { return Complex(x + b.x, y + b.y); } Complex operator*(const Complex &b) const { return Complex(x * b.x - y * b.y, x * b.y + y * b.x); } }; void change(Complex y[], int len) { int i, j, k; for (int i = 1, j = len / 2; i < len - 1; i++) { if (i < j) swap(y[i], y[j]); k = len / 2; while (j >= k) j = j - k, k = k / 2; if (j < k) j += k; } } /* * 做 FFT * len 必须是 2^k 形式 * on == 1 时是 DFT,on == -1 时是 IDFT */ void fft(Complex y[], int len, int on) { change(y, len); for (int h = 2; h <= len; h <<= 1) { Complex wn(cos(2 * PI / h), sin(on * 2 * PI / h)); for (int j = 0; j < len; j += h) { Complex w(1, 0); for (int k = j; k < j + h / 2; k++) { Complex u = y[k]; Complex t = w * y[k + h / 2]; y[k] = u + t; y[k + h / 2] = u - t; w = w * wn; } } } if (on == -1) for (int i = 0; i < len; i++) y[i].x /= len; } Complex x1[maxm], x2[maxm]; int main() { for (scanf("%d", &T); T--;) { scanf("%d%d", &n, &m); for (int i = 0; i < n; ++i) num[i] = ans[i] = 0; scanf("%s", &s); scanf("%s", &t); int len = 1; while (len < n << 1) len <<= 1; for (int j = 0; j < 11; ++j) { for (int i = 0; i < n; ++i) x1[i] = Complex((s[i] == ('0' + j)) || (s[i] == '*' && j == 10), 0); for (int i = n; i < len; ++i) x1[i] = Complex(0, 0); for (int i = 0; i < m; ++i) x2[m - i - 1] = Complex((t[i] == ('0' + j)) || (t[i] == '*') || j == 10, 0); for (int i = m; i < len; ++i) x2[i] = Complex(0, 0); fft(x1, len, 1); fft(x2, len, 1); for (int i = 0; i < len; ++i) x1[i] = x1[i] * x2[i]; fft(x1, len, -1); for (int i = 0; i < n; ++i) num[i] += x1[i].x + 0.5; } for (int i = m - 1; i < n; ++i) ans[m - num[i]]++; for (int i = 0; i <= m; ++i) printf("%d\n", ans[i] += i ? ans[i - 1] : 0); } return 0; } ``` ## 1009 HDU6981 [Rise in Price](https://acm.hdu.edu.cn/showproblem.php?pid=6981) ### 题意 已知 $n\times n$ 的格子,初始在 $(1,1)$ 处,只能向右或向下走,到达 $(n,n)$ ,每个格子里都有重量为 $a_{i,j}$ 的宝石,并且经过每个格子都可以让单位重量宝石的价格提升 $b_{i,j}$,求能得到的所有宝石的最大价格。 ### 思路 因为只会经过 $2n-1$ 个格子,且 $n\le100$ ,数据量较小,因此考虑宽搜剪枝,如果对于位于 $i,j$ 的两个状态 $s_1,s_2$ ,满足 $\sum a_1 < \sum a_2,\sum b_1 < \sum b_2$,则可以将状态 $s_1$剪掉。 ### code ```cpp ```