Portal --> broken qwq
Description
给你一个数组,每个元素有一个颜色,要求支持两种操作:
1、修改某个元素的颜色
2、询问这个数组有多少个自取件内没有重复的颜色
数据范围:(n<=10^5,m<=2n),颜色大小在(1sim 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)+sumlimits_{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("
");
}
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
",Seg::ans[1]);
else{
scanf("%d%d",&x,&to);
update(x,to);
}
//debug();
}
}