题解:P5070 [Ynoi Easy Round 2015] 即便看不到未来

· · 题解

题意

给你一个长度为 n 序列,有 m 次询问,每次查询一段区间中长度为 1,2,\ldots,10 的极长值域连续段(去重排序后)个数。

## 思路 题目没有要求在线,所以考虑离线扫描线。考虑新加入一个数 $a_i$ 对答案的贡献,我们发现,一个数的加入贡献只会和 $(lst_{a_i},i]$ 中值域在 $(a_i - 10, a_i+10)$ 之中的有贡献。所以我们可以暴力的将这些数以及对应的上次出现的位置取出来扩展,复杂度是正确的。思考怎么处理,设 $L_k,R_k$ 为之前扩展后的某个范围的左右端点,现加入一个数 $x$,有一下三种情况: - $x=L_k-1$ 答案减掉长度为 $R_k-L_k+1$ 的贡献,加上长度为 $R_k-(L_k-1)+1$ 即 $R_k-L_k+2$ 的贡献。 - $x=R_k+1$ 答案减掉长度为 $R_k-L_k+1$ 的贡献,加上长度为 $(R_k+1)-L_k+1$ 即 $R_k-L_k+2$ 的贡献。 - $x=R_{k-1}+1$ 并且 $x=L_k-1$ 则答案需要减去 $R_{k-1}-L_{k-1}+1$ 和 $R_k-L_k+1$ 的贡献,加上 $R_k-L_{k-1}+1$ 的贡献。 所以我们需要一个连续段区间修改,单点查询的数据结构,用树状数组维护查分即可。 需要注意一下边界问题,因为 $(a_i-10,a_i+10)$ 的旧贡献被减去去时要使用 $(a_i-11,a_i+11)$,于是 $a_i + 11$ 和 $a_i-11$ 也需要被加入。 时间复杂度 $O(k(n+m)\log n)$,其中 $k$ 是值域段长度,常数较小,目前在最优解第一页。具体细节可以看代码,本题不卡常。 ## 代码 ```cpp const int N = 1e6 + 20; int n, m, a[N], ans[N][15], las[N], Mn, Mx; vector<pii> vec[N]; class BIT{ #define lowbit(x) x&(-x) public : int c[N]; void add(int x, int y){ for(; x < N; x += lowbit(x)) c[x] += y;} int query(int x){ int res = 0; for(; x; x -= lowbit(x)) res += c[x]; return res;} } Tr[22]; int main(){ freopen("data.in","r",stdin); freopen("data.out","w",stdout); n = rd(), m = rd(); Mn = INF; For(i, 1, n) a[i] = rd(), Mn = min(a[i], Mn), Mx = max(Mx, a[i]); For(i, 1, m) {int l = rd();int r = rd(); vec[r].pb({l, i});} vint v; int x, y; For(i, 1, n){ for(int j = max(Mn, a[i] - 11); j <= min(Mx, a[i] + 11); ++j) if(las[j] > las[a[i]]) v.pb(las[j]); v.pb(i); sort(v.begin(), v.end()); int l = a[i], r = a[i]; for(int k = v.size() - 1; k >= 0; --k){ x = v[k], y = k ? v[k - 1] : las[a[i]]; while(a[i] - l <= 10 && las[l - 1] >= x) --l; while(r - a[i] <= 10 && las[r + 1] >= x) ++r; if(r - l + 1 >= 1 && r - l + 1 <= 10) Tr[r - l + 1].add(y + 1, 1), Tr[r - l + 1].add(x + 1, -1); if(r - a[i] >= 1 && r - a[i] <= 10) Tr[r - a[i]].add(y + 1, -1), Tr[r - a[i]].add(x + 1, 1); if(a[i] - l >= 1 && a[i] - l <= 10) Tr[a[i] - l].add(y + 1, -1), Tr[a[i] - l].add(x + 1, 1); } for(auto [l, id] : vec[i]) for(int j = 1; j <= 10; ++j) ans[id][j] = Tr[j].query(l); las[a[i]] = i; v.clear(); } for(int i = 1; i <= m; ++i, putc('\n')){ for(int j = 1; j <= 10; ++j) write(ans[i][j] % 10);} ```