1 条题解
-
0
自动搬运
来自洛谷,原作者为

Alex_Wei
**搬运于
2025-08-24 22:39:01,当前版本为作者最后更新于2022-07-10 13:55:29,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
集合 的装饰子集即不被其它任何数包含的子集, 包含 当且仅当 ,即 为 的位 也为 。
考虑原序列的装饰子集 ,假设 ,因为 不被任何数包含,所以对于任意子串 , 同样不被区间内任何数包含。因此 必然作为某个划分子串的装饰子集内的一个元素。所有子串的装饰子集包含 。
考虑 ,假设存在 包含 。因 不可能作为 所在子串的装饰子集,故所有子串的装饰子集不包含 以外的元素。
这证明了所有子串装饰子集等于 。
令 表示使得 包含所有 内元素的最大的 ,显然可以双指针求出。
容易得到 DP 表示 的答案,。若 存在,则有转移方程 ,表示将 划为子串。前缀和优化即可做到 。
求 相当容易,只需对每个数 检查是否存在 有 包含 ,可以再搞个 DP 算这玩意,也可以直接高维后缀和,相当好写。
时间复杂度 。
#include <bits/stdc++.h> using namespace std; using ll = long long; bool Mbe; constexpr int N = 3e6 + 5; constexpr int mod = 1e9 + 7; void add(int &x, int y) {x += y, x >= mod && (x -= mod);} int n, a[N], f[N], g[N], s[N], cnt, buc[N], exist[N]; bool Med; int main() { fprintf(stderr, "%.4lf\n", (&Mbe - &Med) / 1048576.0); #ifdef ALEX_WEI freopen("1.in", "r", stdin); freopen("1.out", "w", stdout); #endif ios::sync_with_stdio(0); cin >> n; for(int i = 1; i <= n; i++) cin >> a[i], f[a[i]] = exist[a[i]] = 1; for(int d = 2, k = 1; k < 1 << 21; d <<= 1, k <<= 1) for(int i = 0; i < 1 << 21; i += d) for(int j = 0; j < k; j++) f[i | j] += f[i | j | k]; for(int i = 0; i < 1 << 21; i++) cnt += f[i] == 1 && exist[i]; g[0] = s[0] = 1; for(int i = 1, l = 1; i <= n; i++) { s[i] = s[i - 1]; cnt -= !buc[a[i]] && f[a[i]] == 1; buc[a[i]]++; while(f[a[l]] != 1 || buc[a[l]] > 1) buc[a[l++]]--; if(!cnt) g[i] = s[l - 1]; add(s[i], g[i]); } cout << g[n] << "\n"; return cerr << "Time: " << clock() << "\n", 0; }
- 1
信息
- ID
- 7707
- 时间
- 1000~3000ms
- 内存
- 128MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者