P9990 [Ynoi Easy Round 2023] TEST_90 题解
很容易想到离线扫描线。
考虑离线从 到 扫描 ,同时开一棵线段树,线段树每个位置都是 或 ,第 个位置表示 中出现过的数的个数奇偶性。对于 ,默认这个点为 。则每个询问都是扫描到对应的 时刻,区间 的历史版本和。
考虑扫描过程的转移。从前往后扫描时显然可以动态维护 的上一次出现位置 。 内的点,奇偶性不变,后面的点,奇变偶,偶变奇。
于是我们要维护支持区间反转,历史版本和的线段树。每个节点额外开个 ,分别表示当前区间历史版本和,当前区间在目前操作序列中没有被反转几次,被反转几次。
具体看代码注释:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
using namespace std;
const int N = 1e6 + 5;
int n, m, a[N];
long long ans[N];
vector<pair<int, int>> v[N];
class SegmentTree
{
public:
struct Node
{
int l, r;
long long sum;
long long hsum;
bool tag;
long long tg[2];
}tr[N << 2];
void pushup(int u)
{
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
tr[u].hsum = tr[u << 1].hsum + tr[u << 1 | 1].hsum;
}
void pushdown(int u)
{
tr[u << 1].hsum += tr[u << 1].sum * tr[u].tg[0] + tr[u].tg[1] * (tr[u << 1].r - tr[u << 1].l + 1 - tr[u << 1].sum); // 有 tr[u].tg[0] 个没有反转和 tr[u].tg[1] 个有反转
if (tr[u].tag)
{
tr[u << 1].sum = (tr[u << 1].r - tr[u << 1].l + 1) - tr[u << 1].sum; // 正常维护 tag
}
tr[u << 1].tg[0] += tr[u].tg[tr[u << 1].tag], tr[u << 1].tg[1] += tr[u].tg[tr[u << 1].tag ^ 1];
// 如果左儿子处于反转状态,即 tag = 1,那么 tg[0] 应该加上 u 点的 tg[1],tg[1] 同理
tr[u << 1].tag ^= tr[u].tag;
// 右儿子同理
tr[u << 1 | 1].hsum += tr[u << 1 | 1].sum * tr[u].tg[0] + tr[u].tg[1] * (tr[u << 1 | 1].r - tr[u << 1 | 1].l + 1 - tr[u << 1 | 1].sum);
if (tr[u].tag)
{
tr[u << 1 | 1].sum = (tr[u << 1 | 1].r - tr[u << 1 | 1].l + 1) - tr[u << 1 | 1].sum;
}
tr[u << 1 | 1].tg[0] += tr[u].tg[tr[u << 1 | 1].tag], tr[u << 1 | 1].tg[1] += tr[u].tg[tr[u << 1 | 1].tag ^ 1];
tr[u << 1 | 1].tag ^= tr[u].tag;
tr[u].tag = 0;
tr[u].tg[0] = tr[u].tg[1] = 0;
}
void build(int u, int l, int r)
{
tr[u] = { l, r, 0LL, 0LL, 0, {0LL, 0LL} };
if (l == r) return;
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
}
void update(int u, int l, int r)
{
if (tr[u].l >= l and tr[u].r <= r)
{
tr[u].tag ^= 1;
tr[u].tg[tr[u].tag]++;
tr[u].sum = (tr[u].r - tr[u].l + 1) - tr[u].sum;
tr[u].hsum += tr[u].sum;
return;
}
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if (l <= mid) update(u << 1, l, r);
else
{
// 如果另一边不需要反转,也需要记录 hsum 和 tg
tr[u << 1].tg[tr[u << 1].tag]++;
tr[u << 1].hsum += tr[u << 1].sum;
}
if (r > mid) update(u << 1 | 1, l, r);
else
{
// 如果另一边不需要反转,也需要记录 hsum 和 tg
tr[u << 1 | 1].tg[tr[u << 1 | 1].tag]++;
tr[u << 1 | 1].hsum += tr[u << 1 | 1].sum;
}
pushup(u);
}
long long query(int u, int l, int r)
{
if (tr[u].l >= l and tr[u].r <= r)
{
return tr[u].hsum;
}
pushdown(u);
long long res = 0LL;
int mid = tr[u].l + tr[u].r >> 1;
if (l <= mid) res = query(u << 1, l, r);
if (r > mid) res = res + query(u << 1 | 1, l, r);
return res;
}
}sgt;
int lstpos[N];
int main()
{
ios::sync_with_stdio(0), cin.tie(0);
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
cin >> m;
for (int i = 1; i <= m; i++)
{
int l, r;
cin >> l >> r;
v[r].emplace_back(make_pair(i, l));
}
sgt.build(1, 1, n);
for (int i = 1; i <= n; i++)
{
int p = lstpos[a[i]];
lstpos[a[i]] = i;
sgt.update(1, p + 1, i);
for (auto& [id, l] : v[i])
{
ans[id] += sgt.query(1, l, i);
}
}
for (int i = 1; i <= m; i++) cout << ans[i] << "\n";
return 0;
}

浙公网安备 33010602011771号