6911. 【2020.12.01提高组模拟】莫队

一个序列,定义无重区间为没有重复的数的区间。两种操作:

  1. (a_x)改为(y)
  2. 询问区间([x,y])中有多少个无重子区间。

(n,mle 2*10^5)


按照套路求出每个位置前面第一个相同的位置,记为(lst_i),对于一个询问([L,R]),答案为(sum_{i=L}^R i-max(max_{j=L}^{i}lst_j,L-1))

相当于要求([L,R])的前缀(max)的和。

可以规定(lst_L=L-1),这样就不用特别考虑(L-1)的影响,甚至可以直接写作(sum_{i=L}^R i-max_{j=1}^ilst_j)

考虑暴力,相当于一个数(x),初始为(L-1),从(L)做到(R),每次(x o max(x,a_i))(ll称之为复合),并且做一次加(x)的贡献。记这个东西为(query(L,R,x))

先说个(O(nlg^2n))的做法:

用线段树维护。考虑(query(L,R,x)),如果(x>mx(L,mid)),则((mid-l+1)*x+query(mid+1,R,x));否则(query(l,mid,x)+query(mid+1,R,mx(L,mid)))。可以发现(query(mid+1,R,mx(L,mid)))已经和(x)无关了,所以可以预处理(f(L,R)=query(mid+1,R,mx(L,mid)))

在update的时候花(O(lg ))时间维护每一个有影响的(f(L,R)),于是一次修改时间复杂度(O(lg^2))

询问的时候需要找出(O(lg))子区间然后进行(query)操作,(query)执行一次是(O(lg)),所以查询一次时间复杂度(O(lg^2))

再说ll的(O(nlg n))爆标做法:

对于每个时间,按照序列从前往后做,一路取前缀(max)。(如果是询问时间可以将(lst_{L})变为(L-1),变成从(1)开始的前缀max)

把时间变成要维护的维度,枚举序列的位置,维护所有时间的前缀(max)及其贡献历史和。

可以用吉司机线段树解决。


using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
#define N 200005
#define ll long long
int n,m;
int a[N],lst[N];
set<int> buc[N];
int mx[N*4];
ll f[N*4];
ll tquery(int k,int l,int r,int x){
	if (l==r)
		return max(x,mx[k]);
	int mid=l+r>>1;
	if (x>mx[k<<1]) return (ll)x*(mid-l+1)+tquery(k<<1|1,mid+1,r,x);
	return tquery(k<<1,l,mid,x)+f[k];
}
void build(int k,int l,int r){
	if (l==r){
		mx[k]=lst[l];
		return;
	}
	int mid=l+r>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	mx[k]=max(mx[k<<1],mx[k<<1|1]);
	f[k]=tquery(k<<1|1,mid+1,r,mx[k<<1]);
}
void modify(int x,int c,int k=1,int l=1,int r=n){
	if (l==r){
		mx[k]=c;
		return;
	}
	int mid=l+r>>1;
	if (x<=mid) modify(x,c,k<<1,l,mid);
	else modify(x,c,k<<1|1,mid+1,r);
	mx[k]=max(mx[k<<1],mx[k<<1|1]);
	f[k]=tquery(k<<1|1,mid+1,r,mx[k<<1]);
}
ll ans;
void query(int st,int en,int &x,int k=1,int l=1,int r=n){
	if (st<=l && r<=en){
		ans+=tquery(k,l,r,x);
		x=max(x,mx[k]);
		return;
	}
	int mid=l+r>>1;
	if (st<=mid) query(st,en,x,k<<1,l,mid);
	if (mid<en) query(st,en,x,k<<1|1,mid+1,r);
}
int main(){
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	freopen("team.in","r",stdin);
	freopen("team.out","w",stdout);
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;++i){
		scanf("%d",&a[i]);
		if (!buc[a[i]].empty())
			lst[i]=*buc[a[i]].rbegin();
		buc[a[i]].insert(i);
	}
	build(1,1,n);
	for (int i=1;i<=m;++i){
		int op,x,y;
		scanf("%d%d%d",&op,&x,&y);
		if (op==1){
			buc[a[x]].erase(x);
			auto p=buc[a[x]].upper_bound(x);
			if (p!=buc[a[x]].end()){
				if (p==buc[a[x]].begin())
					lst[*p]=0;
				else{
					auto q=p;
					lst[*p]=*(--q);
				}
				modify(*p,lst[*p]);
			}			
			a[x]=y;
			p=buc[y].upper_bound(x);
			if (p!=buc[y].end()){
				lst[*p]=x;
				modify(*p,lst[*p]);
			}
			if (p==buc[y].begin())
				lst[x]=0;
			else
				lst[x]=*(--p);
			modify(x,lst[x]);
			buc[y].insert(x);
		}
		else{
			ans=0;
			int z=x-1;
			query(x,y,z);
			printf("%lld
",(ll)(x+y)*(y-x+1)/2-ans);
		}	
	}
	return 0;
}
原文地址:https://www.cnblogs.com/jz-597/p/14070633.html