题目链接 link
(转载)(看不懂)
我们用 \(x\) 表示输入的数组,\(y\) 表示变化后的数组,用 \(x[l:r]\) 表示数组 \(x\) 的区间 \([l,r]\) 形成的子数组。
对于两个区间 \([l_1,r_1]\) 和 \([l_2,r_2]\),如果 \(x[l_1:r_1] \neq x[l_2,r_2]\),那么 \(y[l_1:r_1]\) 必定也不等于 \(y[l_2:r_2]\)。所以我们可以把 \(x\) 的每一个本质不同的子区间分开来考虑。
对于一个长度为 \(k\) 的子数组 \(z\),假设它在 \(x\) 出现了 \(t\) 次,这 \(t\) 次出现的左端点下标从左到右依次为 \(a_1, \cdots, a_t\)。我们只需要计算这 \(t\) 次出现在 \(N\) 种可能的数组中一共贡献了多少本质不同的子串,最终的答案就是所有本质不同的子区间的贡献之和。
考虑容斥,对于 \(A = \{a_1, \cdots, a_t \}\) 的每一个非空子集 \(B = \{b_1, \cdots, b_w\}\),我们计算有多少种可能的 \(y\) 让这 \(w\) 次出现都相同(记作 \(c(B)\)),那么子数组 \(z\) 对答案的贡献就是:
先考虑如何计算 \(c(B)\)。在 \(B\) 给定的情况下,\(x\) 的每一个位置都有两种可能:被某一次出现(即某个 \([b_i: b_i+k-1]\) )覆盖到了或者没有被任何一次出现覆盖。对于后者,这个位置的值可以任意选取,它对答案的贡献是乘上 \(m\)。所以我们只需要考虑第一种情况就行。
对于第一种情况的所有位置,如果 \(y[b_1:b_1+k-1]\) 的值已经确定了,那么所有第一种情况的位置的值都已经确定了。但是问题在于有些 \(y[b_1:b_1+k-1]\) 的值不合法。比如说 \(n=3,k=2,B=\{1,2\}\),那么 \(y[1:2]\) 中的两个字符必须相等,比如串 \(y[1:2]\) 肯定不等于 \(y[2:3]\)。
考虑相邻的两次出现 \(b_i\) 和 \(b_{i+1}\),如果这两次出现相交了,即 \(b_i + k -1 \ge b_{i+1}\),那么就对 \(y[b_1:b_1+k-1]\) 产生了一个约束:这个串的长度为 \(b_i+k-b_{i+1}\) 的后缀和前缀必须相等,即存在长度为 \(b_{i}+k-b_{i+1}\) 的 border。要计算 \(c(B)\),只需要把所有限制都考虑进来,这些限制把 \(y[b_1:b_1+k-1]\) 的位置划分成了若干个等价类,每一个等价类的值都是可以任意取的,因此如果有 \(i\) 个等价类,那么第一类位置的取值方案数就是 \(m^i\)。
我们令划分出的等价类为一个串的“状态”,\(f(n)\) 表示长度为 \(n\) 的字符串的不同状态数。举例来说,\(f(3) = 3\),它们分别是三个位置都不要求相同,第一个位置和第三个位置必须相同,三个位置必须都相同。一个有趣的结论是,\(f(n)\) 的大小并不是很大:\(f(50) = 2249, f(100)=35200\)。所以我们可以考虑把当前的等价类划分给压进动态规划的状态里。
我们考虑从左到右依次把 \(A\) 中的元素给加入到 \(B\) 里,令 \(g[i][j][S]\) 表示当前考虑到了 \(a_i\) 并把 \(a_i\) 加入到了 \(B\) 中,当前 \(B\) 的大小是 \(j\),等价类状态是 \(S\) 时的贡献,那么每一次就是枚举下一个加入 \(B\) 中的位置 \(a_t\),然后根据 \(a_t\) 与 \(a_i\) 之间是否重叠、重叠长度来更新 \(S\),把对应的贡献转移到 \(g[t][j+1][S']\)。
直接这么写可能会超时,需要用两个 trick 来进行优化:
- 我们并不关心 \(B\) 的大小,只关心 \(B\) 大小的奇偶,因此在状态里只需要记录大小的奇偶性就行。
- 当 \(k\) 比较大的时候,一些状态是不可达的:最长的重叠长度只有 \(n-k\)。排除掉所有不可达的等价状态可以加速。
#include <iostream>
#include <vector>
#include <unordered_set>
#include <unordered_map>
#include <string>
using namespace std;const int MOD = 998244353;// 计算所有本质不同子区间的数量
int countDistinctSubarrays(const vector<int>& arr) {unordered_set<int> uniqueSubarrays;int n = arr.size();for (int i = 0; i < n; ++i) {int subarray=0;for (int j = i; j < n; ++j) {subarray = subarray*10+arr[j];uniqueSubarrays.insert(subarray);}}return uniqueSubarrays.size();
}int main() {int t;cin >> t;while (t--) {int n, m;cin >> n >> m;vector<int> x(n);for (int i = 0; i < n; ++i) {cin >> x[i];}unordered_map<string, int> dp;dp[""] = 1; // 初始状态long long totalComplexity = 0;for (int i = 0; i < n; ++i) {unordered_map<string, int> newDp;for (const auto& [key, value] : dp) {for (int j = 1; j <= m; ++j) {string newKey = key + to_string(x[i] * m + j) + ",";newDp[newKey] = (newDp[newKey] + value) % MOD;}}dp.swap(newDp);}for (const auto& [key, value] : dp) {vector<int> arr;size_t start = 0;size_t end = key.find(",");while (end != string::npos) {arr.push_back(stoi(key.substr(start, end - start)));start = end + 1;end = key.find(",", start);}totalComplexity = (totalComplexity + (long long)countDistinctSubarrays(arr) * value) % MOD;}cout << totalComplexity << endl;}return 0;
}