Educational Codeforces Round 151
AB
略
C(简)
将密码 \(P\) 与 \(S\) 进行匹配,按顺序决定 \(P_i\),为了避免 \(P\) 成为 \(S\) 的子串,每次贪心地选择当前匹配位置最靠后的。若出现匹配不上则“YES”。
D
有点意思。从基础的情况入手:
设 \(\{s_i\}\) 为 \(\{a_i\}\) 的前缀和,弄出 \(\{s_i\}\) 的图像,让我们考虑第一个山峰(极大值)\(s_x\)。显然,若 \(k<s_x\),最后的结果不会优于 \(k=s_x\),因为一口气就爬到 \(s_x\) 了,在下面挡不如在 \(s_x\) 挡掉的多。
已知 \(k\geq s_x\),找到下一座比 \(s_x\) 高的山峰 \(s_y\)。同样的,若 \(s_x<k<s_y\),则不会优于 \(k=s_y\),因为一旦在位置 \(p\) 超过 \(s_x\),\(\{s_i\}\) 就会在 \([p,y]\) 上单调递增(毕竟在 \((x,y)\) 上没有其他高于 \(s_x\) 的山峰),挡在下边不如挡在 \(s_y\)。
根据以上推论,\(k\in \{s_i\}\),且必定是其中的前缀最大值。于是可以枚举 \(k\) 来计算答案:设 \(k=s_u\),因为 \(s_u\) 为前缀最大值,所以 \(k\) 不会对 \([1,u]\) 产生影响,只需考虑 \(k\) 对后面的贡献。而 \(k\) 对最终答案的作用只与 \(s[u+1,n]\) 中的最小值 \(min\) 有关,即让答案增加了 \(k-min\)。简单预处理即可。
E(废话连篇版)
球的移动方向并不固定,可以来回动,直接DP似乎具有后效性,无法计算。
仔细考虑这个问题,如果某个状态可以达到,需要满足什么条件?首先,球的数量要相同;其次,需要满足奇偶性的限制:令 \(S\) 为有球的箱子的序号之和,每移动一次改变 \(S\) 的奇偶,因此 \(S\) 最终的奇偶性固定;最后,\(k\) 要足够大以完成移动。
对于后面两个条件,可以算出移动的最小步数 \(k'\),判断 \(k\) 与 \(k'\) 奇偶性相同且 \(k'\leq k\)。而 \(k'\) 的计算只需把初始状态和最终状态的每个球按顺序一一对应,将位置差相加。
所以不妨令 \(f_{i,j,s}\) 表示考虑了前 \(i\) 个球,第 \(i\) 个球最终在位置 \(j\ (\ge i)\) ,前 \(i\) 个球最少要用 \(s\) 步的方案数。直接枚举第 \(i + 1\) 个球的位置进行转移,复杂度 \(O(n^2k\times n)=O(n^3k)\)。但得知第 \(i\) 个球的具体位置没啥卵用,稍加修改,令 \(j\) 表示第 \(i\) 个球放在 \(\le j\) 的位置,只要讨论 \(j+1\) 有没有球就能 \(O(1)\) 转移,复杂度变为 \(O(n^2k)\)。
以上均为废话......退役一年半,说话变啰嗦了。接下来考虑关键的优化。
注意到 \(k\) 只有 \(n\) 的数量级,很多不可能的情况可以直接pass。具体的,若初始状态为 \(A\),末状态为 \(B\),\(A\) 的前 \(i\) 个箱子中有 \(c\) 个球,那么 \(B\) 的前 \(i\) 个箱子中球的个数在区间 \([c-\sqrt k,c+\sqrt k]\) 内。于是,复杂度优化为 \(O(nk\sqrt k)\)。
#include <bits/stdc++.h>
using namespace std;
void read (int &x) {
char ch = getchar(); x = 0; while (!isdigit(ch)) ch = getchar();
while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
} const int N = 1505, mod = 1e9 + 7;
int n, k, tot, w[N], a[N], c[N];
// int f[N][80][N]; 内存过大,滚动优化
int f[2][80][N];
#define add(x, y) ((x += y) >= mod && (x -= mod))
signed main() {
read (n), read (k);
for (int i = 1; i <= n; ++i) read (a[i]);
for (int i = 1; i <= n; ++i) {
c[i] = c[i - 1]; if (a[i]) ++c[i], w[++tot] = i;
}
f[0][40][0] = 1;
int m = (int)pow (k, 0.5);
for (int i = 1; i <= n; ++i) {
int I = (i & 1); memset (f[I], 0, sizeof (f[I]));
for (int j = 40 - m; j <= 40 + m; ++j) {
for (int s = 0; s <= k; ++s) {
add (f[I][j][s], f[I ^ 1][j + a[i]][s]);
if (c[i] + j - 40 > 0 && c[i] + j - 40 <= tot && s >= abs (i - w[c[i] + j - 40]))
add (f[I][j][s], f[I ^ 1][j + a[i] - 1][s - abs (i - w[c[i] + j - 40])]);
}
}
}
int ans = 0;
for (int i = k; i >= 0; i -= 2) add (ans, f[n & 1][40][i]);
printf ("%d\n", ans % mod);
return 0;
}