【2016北京集训】数组

Portal --> broken qwq

Description

​  给你一个数组,每个元素有一个颜色,要求支持两种操作:

1、修改某个元素的颜色

2、询问这个数组有多少个自取件内没有重复的颜色

​​  数据范围:\(n<=10^5,m<=2n\),颜色大小在\(1\sim n\)之间

​  

Solution

​​  这题。。本来应该是一个树套树题

​​  但是为什么一定要用树套树呢对吧qwq

​​  首先是套路:考虑维护一个\(pre\)数组,表示每个节点的前一个最近的和它颜色一样的节点,那么我们考虑固定一个右端点,将左端点往左移,直到再往前移一位就会导致当前区间内的\(pre\)的最大值在这个区间内,这个时候区间的长度就是这个右端点对答案的贡献

​  然而。。直接这样做是不行的因为我们要支持修改和多组询问qwq

​  所以这里考虑用线段树维护一个神秘的东西:对于线段树中的一个节点,假设它对应的区间是\([l,r]\),那么我们维护一个\(mx\)\(ans\),分别表示当前区间内\(pre\)的最大值,以及,只考虑当前区间的\(pre\)限制的右端点贡献之和(这个概念描述起来有点神秘,具体一点就是:首先这个\(ans\)记录的是该区间中的每个点作为右端点算得的贡献之和,但是这个贡献在计算的时候,只考虑当前区间内的\(pre\)的影响,更加直观一点来说就是可以理解为移动左端点求\(pre\)的最大值的时候,如果说新加进来的位置不在\([l,r]\)区间内,就不取\(max\)),然后答案就应该是线段树根节点的\(ans\)值了,对于叶子节点来说\(ans=x-pre[x]\),其中\(x\)是这个叶子节点在数组中对应的位置

​  现在考虑怎么维护这个东西

​  为了方便接下来的描述,约定用\(x\)表示当前区间,\(L\)表示当前区间的左儿子(左半部分),\(R\)表示当前区间的右儿子(右半部分),然后我们考虑怎么用\(ans[L]\)\(ans[R]\)求得\(ans[x]\),这里需要根据\(mx[L]\)\(mx[R]\)的大小关系进行一些讨论,首先我们先看最简单的情况:

(1)如果说\(mx[L]>=mx[R]\):首先\(ans[L]\)肯定还是会作为\(ans[x]\)的一部分的,因为加入的东西在后面,不会影响\(ans[L]\)的贡献,然后我们看\([mid+1,r]\)区间中的元素,因为\(mx[R]<=mx[L]\),也就是说后面的元素作为右端点的时候左端点停下的地方肯定在\(mx[L]+1\)这个位置,所以我们可以直接计算贡献:\(ans[x]=ans[L]+(mid-mx[L])*(r-mid)+\sum\limits_{i=1}^{r-mid}i\),具体一点的话就是\([mid+1,r]\)中的每个元素对应的左端点可以先走到\(mid+1\)这个位置,再走到\(mx[L]+1\)的位置

​  

​​  接下来看复杂一点的另一种情况:

(2)如果说\(mx[L]<mx[R]\),那么说明\([mid+1,r]\)中有一部分的元素对应的左端点可以走到\(mx[L]+1\),有的在\([mid+1,r]\)中的某个位置就停下了,这个时候我们就不能直接计算答案了,由于\(pre\)\(max\)值在左端点的移动过程中是递增的,所以我们可以考虑递归求解,在递归求解的时候也是通过这个右儿子左儿子的\(mx\)值和\(mx[L]\)进行比较,如果可以直接计算答案就直接计算,否则继续递归,遇到叶子的话也是返回,因为只有一个节点了可以直接计算

​  

​  最后就是\(pre\)的修改,我们只要对每个值用一个\(set\)随便维护一下下标位置就好啦

​  

​​  代码大概长这个样子

#include<iostream>
#include<cstdio>
#include<cstring>
#include<set>
#define ll long long
using namespace std;
const int N=1e5+10,SEG=N*4;
int pre[N],a[N];
set<int> rec[N];
set<int>::iterator it,fr,nxt;
int n,m;
ll sum(int l,int r){return 1LL*(l+r)*(r-l+1)/2;}
namespace Seg{/*{{{*/
	int ch[SEG][2],mx[SEG];
	ll ans[SEG];
	int n,tot;
	ll calc(int x,int l,int r,int L){
		int mid=l+r>>1;
		if (l==r) return l-max(mx[L],mx[x]);
		if (mx[ch[x][0]]<=mx[L])
			return 1LL*(l-1-mx[L])*(mid-l+1)+sum(1,mid-l+1)+calc(ch[x][1],mid+1,r,L);
		else
			return (ans[x]-ans[ch[x][0]])+calc(ch[x][0],l,mid,L);
	}
	void pushup(int x,int l,int r){
		int mid=l+r>>1;
		mx[x]=max(mx[ch[x][0]],mx[ch[x][1]]);
		ans[x]=ans[ch[x][0]];
		if (mx[ch[x][0]]>=mx[ch[x][1]])
			ans[x]+=1LL*(mid-mx[ch[x][0]])*(r-mid)+sum(1,r-mid);
		else
			ans[x]+=calc(ch[x][1],mid+1,r,ch[x][0]);
	}
	void _build(int x,int l,int r){
		mx[x]=0;
		if (l==r){
			mx[x]=pre[l]; ans[x]=l-pre[l];
			return;
		}
		int mid=l+r>>1;
		ch[x][0]=++tot; _build(ch[x][0],l,mid);
		ch[x][1]=++tot; _build(ch[x][1],mid+1,r);
		pushup(x,l,r);
	}
	void build(int _n){n=_n; tot=1; _build(1,1,n);}
	void _update(int x,int d,int lx,int rx){
		if (lx==rx){
			mx[x]=pre[lx]; ans[x]=lx-pre[lx];
			return;
		}
		int mid=lx+rx>>1;
		if (d<=mid) _update(ch[x][0],d,lx,mid);
		else _update(ch[x][1],d,mid+1,rx);
		pushup(x,lx,rx);
	}
	void update(int d){_update(1,d,1,n);}
	void debug(int x,int l,int r){
		if (l==r){printf("%lld ",ans[x]); return;}
		int mid=l+r>>1;
		debug(ch[x][0],l,mid);
		debug(ch[x][1],mid+1,r);
	}
	void debug(){debug(1,1,n);}
}/*}}}*/
void update(int x,int delta){
	it=rec[a[x]].find(x);
	fr=it; nxt=it; --fr; ++nxt;
	if (nxt!=rec[a[x]].end()){
		pre[*nxt]=*fr;
		Seg::update(*nxt);
	}
	rec[a[x]].erase(x); 
	a[x]=delta;
	rec[a[x]].insert(x);

	it=rec[a[x]].find(x);
	fr=it; nxt=it; --fr; ++nxt;
	if (nxt!=rec[a[x]].end()){
		pre[*nxt]=x;
		Seg::update(*nxt);
	}
	pre[x]=*fr;
	Seg::update(x);
}
void debug(){
	Seg::debug();
	printf("\n");
}

int main(){
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
#endif
	int op,x,to;
	scanf("%d",&n);
	for (int i=1;i<=n;++i) rec[i].insert(0);
	for (int i=1;i<=n;++i){
		scanf("%d",a+i);
		pre[i]=*(--rec[a[i]].end());
		rec[a[i]].insert(i);
	}
	Seg::build(n);
	scanf("%d",&m);
	for (int i=1;i<=m;++i){
		scanf("%d",&op);
		if (op==0) 
			printf("%lld\n",Seg::ans[1]);
		else{
			scanf("%d%d",&x,&to);
			update(x,to);
		}
		//debug();
	}
}
posted @ 2018-09-28 21:57  yoyoball  阅读(136)  评论(0编辑  收藏  举报