给出一个长度为\(n\)的序列\(a\)。
每次询问一个区间\([l,r]\)。
询问有多少个子区间\([i,j]\)满足子区间内不同的数的数量是奇数。
做法:
将询问离线,从左往右扫描右端点\(r\),维护\([1,r]\)这段序列的合法后缀的数量、不合法后缀的数量。
当前右端点的值是\(a[r]\),记\(a[r]\)的上次出现位置\(pre\),那么\(r\)从上一格移到这一格,\(pre+1\)到\(r\)这段区间的后缀,全都要取反,即合法变为不合法,不合法变为合法。
然后对于当前右端点,在线段树上查询区间和\([l,r]\),就可以得到有多少个右端点是\(r\)的合法区间。
这里有一个非常重要的技巧,对线段树的每个区间维护历史版本和,因为历史上的每个版本都对应一个\(r\)。
同时查询区间历史版本和\([l,r]\),就可以得到有多少个子区间\([i,j]\)满足\(l\leq i\leq j\leq r\),且子区间内的不同数的数量是奇数。
接下来就需要线段树支持区间取反、区间求历史版本和。
这里我只能借鉴了LGM的代码,自己实在是写不出来啊
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=5e5+10;
struct node {
int l,r;
int rev;
int tag0,tag1;
int cnt;
ll sum;
}segTree[maxn<<2];
void add_rev (int u) {
segTree[u].cnt=segTree[u].r-segTree[u].l+1-segTree[u].cnt;
swap(segTree[u].tag0,segTree[u].tag1);
segTree[u].rev^=1;
}
void add_tag0 (int u,int x) {
segTree[u].sum+=1ll*x*(segTree[u].r-segTree[u].l+1-segTree[u].cnt);
segTree[u].tag0+=x;
}
void add_tag1 (int u,int x) {
segTree[u].sum+=1ll*x*segTree[u].cnt;
segTree[u].tag1+=x;
}
void pushdown (int u) {
if (segTree[u].rev) {
add_rev(u<<1);
add_rev(u<<1|1);
segTree[u].rev=0;
}
if (segTree[u].tag0) {
add_tag0(u<<1,segTree[u].tag0);
add_tag0(u<<1|1,segTree[u].tag0);
segTree[u].tag0=0;
}
if (segTree[u].tag1) {
add_tag1(u<<1,segTree[u].tag1);
add_tag1(u<<1|1,segTree[u].tag1);
segTree[u].tag1=0;
}
}
void pushup (int u) {
segTree[u].sum=segTree[u<<1].sum+segTree[u<<1|1].sum;
segTree[u].cnt=segTree[u<<1].cnt+segTree[u<<1|1].cnt;
}
void build (int u,int l,int r) {
segTree[u].l=l;
segTree[u].r=r;
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 (segTree[u].l>=l&&segTree[u].r<=r) {
add_rev(u);
return;
}
pushdown(u);
int mid=(segTree[u].l+segTree[u].r)>>1;
if (l<=mid) update(u<<1,l,r);
if (r>mid) update(u<<1|1,l,r);
pushup(u);
}
ll query (int u,int l,int r) {
if (segTree[u].l>=l&&segTree[u].r<=r) {
return segTree[u].sum;
}
pushdown(u);
int mid=(segTree[u].l+segTree[u].r)>>1;
ll ans=0;
if (l<=mid) ans+=query(u<<1,l,r);
if (r>mid) ans+=query(u<<1|1,l,r);
return ans;
}
vector<pair<int,int> > g[maxn];
ll ans[maxn];
int n,m,a[maxn],pre[maxn];
int main () {
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",a+i);
scanf("%d",&m);
for (int i=1;i<=m;i++) {
int l,r;
scanf("%d%d",&l,&r);
g[r].push_back({l,i});
}
build(1,1,n);
for (int i=1;i<=n;i++) {
update(1,pre[a[i]]+1,i);
add_tag1(1,1);
for (auto it: g[i]) {
int x=it.first;
int y=it.second;
ans[y]=query(1,x,i);
}
pre[a[i]]=i;
}
for (int i=1;i<=m;i++) printf("%lld\n",ans[i]);
}