1 条题解
-
0
给定一个长度为 ,值域为 的序列 ,求出有多少个 的子区间存在绝对众数。
。
注意到绝对众数,每个区间只会存在一个绝对众数,那么不妨把每一个 存在绝对众数的 区间的贡献放在其绝对众数处计算!不妨钦定它为 ,那么现在就是要求出有多少个区间以 为绝对众数,答案即是对所有 求和。
现在对一个 考虑:把 中等于 的数视为 ,否则视为 ,记这个转化后的值为 。则一个区间以 为绝对众数当且仅当区间 之和 。记 表示 ,即 的区间和。
到这一步,其实对一个 已经可以直接跑一遍逆序对求出答案了,时间复杂度 。根据 序列逆序对做法可以做到 (维护移动过程 ,计算移动后多出/减少的贡献,这个可以直接用桶 记录贡献)。
我们考察哪些位置可能被一个和 的区间 覆盖,若 ,那么 即为一种方案,否则若存在一个合法的 满足 ,则 中必有一个 。综上, 能够被一个 的区间覆盖的必要条件是 为端点的最大区间和 。
考虑如何求出所有 ,以 为右端点时为例,此时就是要求出所有前缀 满足最大后缀 。考虑从左到右扫一遍,维护当前的最大后缀 。每次在末尾加入 等价于 ,记 表示序列中 的个数,容易发现 的值最大达到 ,因此这种情况合法的总位置数是 的。同理, 为左端点时同样也只有 ,因此直接暴力正反两遍扫描求得所有 即可(连续的 -1 段可以直接暴力跳,根据前面对总位置数的分析这样是对的),使用归并排序求的最后的位置序列可以做到线性。
那么有可能成为一个 的区间端点的 的个数也是 的。
直接把这些位置拿出来直接求逆序对就可以通过了,时间复杂度 。
如何做到线性?注意到对于一个合法的连续段,其内的所有 都会被拿出来,因此对于所有位置相邻的有效位置段,内部都可以用前面介绍的 序列的线性做法求解,故而做到线性。
如果不写线性代码可以短很多,下面给出一份严格线性的实现:
const int N = 1.5e6 + 5; int n, k, p[N], len, a[N], c[N]; vector<int> b1, b2, b; vector<int> pos[N]; int t1[N], t2[N]; void solve() { cin >> n >> k; rep(i, 1, n) { int x; cin >> x; pos[x].pb(i); a[i] = x; } LL ans = 0; rep(v, 1, k) { if (!SZ(pos[v])) continue; len = SZ(pos[v]); p[len + 1] = n + 1; rep(i, 1, len) p[i] = pos[v][i - 1]; b.clear(), b1.clear(), b2.clear(); int now = 0; rep(i, 1, len) { int x = p[i] + 1; b1.pb(p[i]); now += 1; while (x < p[i + 1] && now >= 0) b1.pb(x), ++x, --now; tomax(now, 0); } now = 0; per(i, len, 1) { int x = p[i] - 1; b2.pb(p[i]); now += 1; while (x > p[i - 1] && now >= 0) b2.pb(x), --x, --now; tomax(now, 0); } reverse(all(b2)); int i = 0, j = 0; while (i < SZ(b1) && j < SZ(b2)) { if (b1[i] < b2[j]) { if (b.empty() || b1[i] != b[SZ(b) - 1]) b.pb(b1[i]); ++i; } else { if (b.empty() || b2[j] != b[SZ(b) - 1]) b.pb(b2[j]); ++j; } } while (i < SZ(b1)) { if (b.empty() || b1[i] != b[SZ(b) - 1]) b.pb(b1[i]); ++i; } while (j < SZ(b2)) { if (b.empty() || b2[j] != b[SZ(b) - 1]) b.pb(b2[j]); ++j; } j = 0; for (auto x: b) { while (j < len && p[j + 1] <= x) ++j; t1[x] = j - (x - j) + n; t2[x] = t1[x] - (a[x] == v? 1: -1); } int lst = 0, nowv = 1, w = 0; for (auto x: b) { if (x ^ (lst + 1)) nowv = t1[x], w = 0; lst = x; ++c[t2[x]], w += (t2[x] < nowv); while (nowv < t1[x]) w += c[nowv++]; while (nowv > t1[x]) w -= c[--nowv]; ans += w; } for (auto x: b) c[t2[x]] = 0; } cout << ans << '\n'; }
- 1
信息
- ID
- 10348
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者