LG P9990 [Ynoi Easy Round 2023] TEST_90 Solution
Description
给定序列 a = ( a 1 , a 2 , ⋯ , a n ) a=(a_1,a_2,\cdots,a_n) a=(a1,a2,⋯,an).
定义 f ( l , r ) = ∣ { a i : i ∈ [ l , r ] } ∣ f(l,r)=|\{a_i:i\in[l,r]\}| f(l,r)=∣{ai:i∈[l,r]}∣.
给定 m m m 次询问 ( l , r ) (l,r) (l,r),你要求出 ∑ l ≤ s ≤ t ≤ r ( f ( s , t ) m o d 2 ) \sum\limits_{l\le s\le t\le r}(f(s,t)\bmod 2) l≤s≤t≤r∑(f(s,t)mod2).
Limitations
1 ≤ n , m ≤ 10 6 1\le n,m\le 10^6 1≤n,m≤106
1 ≤ a i , l , r ≤ n 1\le a_i,l,r\le n 1≤ai,l,r≤n
3 s , 512 MB 3\text{s},512\text{MB} 3s,512MB
Solution
在线做是做不了的,离线下来,挂在 r r r 上扫描线.
设 l s t i lst_i lsti 表示 i i i 前上一个 a i a_i ai 的出现位置.
考虑加入 a i a_i ai 后的影响,区间 [ l , i ] [l,i] [l,i] 的颜色数奇偶性会改变,其中 l ∈ ( l s t i , i ] l\in(lst_i,i] l∈(lsti,i].
显然这就是历史和,现在只需一个支持区间异或 1 1 1,区间历史和的 ds
.
考虑线段树,节点上维护 1 1 1 的个数 cnt \textit{cnt} cnt 与历史和 his \textit{his} his,以及异或标记 rev \textit{rev} rev 与 0 / 1 0/1 0/1 的历史和更新标记 tag 0 / 1 \textit{tag}_{0/1} tag0/1.
这些都是容易直接维护的(具体见代码),于是做完了,时间复杂度 O ( ( n + m ) log n ) O((n+m)\log n) O((n+m)logn).
注意要先下传异或标记,否则答案会错,以及开 long long
和常数问题.
Code
3.64 KB , 11.96 s , 153.59 MB (in total, C++20 with O2) 3.64\text{KB},11.96\text{s},153.59\text{MB}\;\texttt{(in total, C++20 with O2)} 3.64KB,11.96s,153.59MB(in total, C++20 with O2)
#include <bits/stdc++.h>
using namespace std;using i64 = long long;
using ui64 = unsigned long long;
using i128 = __int128;
using ui128 = unsigned __int128;
using f4 = float;
using f8 = double;
using f16 = long double;template<class T>
bool chmax(T &a, const T &b){if(a < b){ a = b; return true; }return false;
}template<class T>
bool chmin(T &a, const T &b){if(a > b){ a = b; return true; }return false;
}namespace fastio {} // By pystraf
using fastio::read;
using fastio::write;namespace seg_tree {inline int ls(int u) { return 2 * u + 1; }inline int rs(int u) { return 2 * u + 2; }struct Node {int l, r, len, cnt;i64 his, tag0 = 0, tag1 = 0;bool rev = false;};struct SegTree {vector<Node> tr;inline SegTree() {}inline SegTree(int n) : tr(n << 1) { build(0, 0, n - 1); }inline void pushup(int u, int mid) {tr[u].his = tr[ls(mid)].his + tr[rs(mid)].his;tr[u].cnt = tr[ls(mid)].cnt + tr[rs(mid)].cnt;}inline void neg(int u) {tr[u].rev ^= 1;swap(tr[u].tag0, tr[u].tag1);tr[u].cnt = tr[u].len - tr[u].cnt;}inline void apply(int u, i64 t0, i64 t1) {tr[u].his += t1 * tr[u].cnt + t0 * (tr[u].len - tr[u].cnt);tr[u].tag0 += t0, tr[u].tag1 += t1;}inline void pushdown(int u, int mid) {if (tr[u].rev) {neg(ls(mid)), neg(rs(mid));tr[u].rev = false;}if (tr[u].tag0 == 0 && tr[u].tag1 == 0) return;apply(ls(mid), tr[u].tag0, tr[u].tag1);apply(rs(mid), tr[u].tag0, tr[u].tag1);tr[u].tag0 = tr[u].tag1 = 0;}void build(int u, int l, int r) {tr[u].l = l, tr[u].r = r;tr[u].len = r - l + 1;if (l == r) return;const int mid = (l + r) >> 1;build(ls(mid), l, mid), build(rs(mid), mid + 1, r);}void negate(int u, int l, int r) {if (l > r) return;if (l <= tr[u].l && tr[u].r <= r) return neg(u);const int mid = (tr[u].l + tr[u].r) >> 1;pushdown(u, mid);if (l <= mid) negate(ls(mid), l, r);if (r > mid) negate(rs(mid), l, r);pushup(u, mid);}i64 query(int u, int l, int r) {if (l <= tr[u].l && tr[u].r <= r) return tr[u].his;const int mid = (tr[u].l + tr[u].r) >> 1;i64 res = 0;pushdown(u, mid);if (l <= mid) res += query(ls(mid), l, r);if (r > mid) res += query(rs(mid), l, r);return res;}};
}
using seg_tree::SegTree;
using pii = pair<int, int>;signed main() {ios::sync_with_stdio(0);cin.tie(0), cout.tie(0);const int n = read<int>();vector<int> a(n), lst(n), mp(n, -1);for (int i = 0; i < n; i++) {a[i] = read<int>(), a[i]--;lst[i] = mp[a[i]];mp[a[i]] = i;}const int m = read<int>();vector<vector<pii>> adj(n);for (int i = 0, l, r; i < m; i++) {l = read<int>(), r = read<int>(), l--, r--;adj[r].emplace_back(l, i);}SegTree sgt(n);vector<i64> ans(m);for (int i = 0; i < n; i++) {sgt.negate(0, lst[i] + 1, i);sgt.apply(0, 0, 1);for (auto [j, id] : adj[i]) ans[id] = sgt.query(0, j, i);}for (int i = 0; i < m; i++) {write(ans[i]);putchar_unlocked('\n');}return 0;
}