1 条题解
-
0
题意
给定长度为 的非负整数数组 ,将其分割成若干连续非空段。设第 段所有元素的按位或值为 ,要求:
求不同的分割方案数,对 取模。
算法
设 表示以 结尾的合法分割方案数,。
。对于固定的右端点 ,随着左端点 从 向左移动, 最多变化 次(因为二进制位只会从 变 一次)。
因此可以维护一个 列表,每个元素为 ,表示:- 对于所有 ,,
- 且 从左到右严格递增。
转移方法:
对于 中的第 段 (其中 是该段最小的 ),设 为下一段的 (最后一段 )。
则 的取值范围是 。
对于这些 ,以 结尾的上一段的 OR 值一定 (因为 且 OR 值随 减小而增大),因此条件自动满足。
所以该段对 的贡献为 $\displaystyle \sum_{j=L-1}^{R-1} dp[j] = prefix[R-1] - prefix[L-2]$。将 从后向前遍历(即 OR 值从小到大),累加即可得到 。
复杂度
- 每个 的 长度 ,
- 总时间复杂度 ,
- 空间复杂度 。
完整代码
#include <bits/stdc++.h> using namespace std; const int MOD = 998244353; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; cin >> n; vector<int> a(n + 1); for (int i = 1; i <= n; i++) { cin >> a[i]; } vector<int> dp(n + 1, 0); dp[0] = 1; vector<int> prefix(n + 1, 0); prefix[0] = 1; vector<pair<int, int>> segments; // (or_value, left_boundary) segments.reserve(30); for (int i = 1; i <= n; i++) { vector<pair<int, int>> new_segments; new_segments.emplace_back(a[i], i); for (auto [val, left] : segments) { int new_val = val | a[i]; if (new_segments.back().first == new_val) { new_segments.back().second = left; } else { new_segments.emplace_back(new_val, left); } } segments = move(new_segments); int total = 0; for (int j = (int)segments.size() - 1; j >= 0; j--) { int left = (j == 0 ? 1 : segments[j - 1].second + 1); int right = segments[j].second; if (left > right) continue; int sum = (prefix[right - 1] - (left - 2 >= 0 ? prefix[left - 2] : 0) + MOD) % MOD; total = (total + sum) % MOD; } dp[i] = total; prefix[i] = (prefix[i - 1] + dp[i]) % MOD; } cout << dp[n] << "\n"; return 0; }
样例解释
样例1
输入:3 1 2 3输出:4
所有 种分割均合法。样例2
输入:5 1000 1000 1000 1000 1000输出:16
所有 种分割均合法。样例3
输入:3 3 4 6输出:3
合法分割:[3][4][6],[3][4,6],[3,4,6]。
- 1
信息
- ID
- 7152
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 1
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者