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

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

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

\(n,m\le 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\to \max(x,a_i)\)(ll称之为复合),并且做一次加\(x\)的贡献。记这个东西为\(query(L,R,x)\)

先说个\(O(n\lg^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(n\lg 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\n",(ll)(x+y)*(y-x+1)/2-ans);
		}	
	}
	return 0;
}
posted @ 2020-12-01 20:44  jz_597  阅读(499)  评论(0编辑  收藏  举报