GYM103069G. Prof. Pang's sequence

给出一个长度为\(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]);
}
原文地址:https://www.cnblogs.com/zhanglichen/p/15534126.html