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) lstr(f(s,t)mod2).

Limitations

1 ≤ n , m ≤ 10 6 1\le n,m\le 10^6 1n,m106
1 ≤ a i , l , r ≤ n 1\le a_i,l,r\le n 1ai,l,rn
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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值