原题
题意
初始有一个长度为n的序列,进行两种操作
1. 修改一个位置上的数。
2. 查询l、r区间内每个值出现的长度之和。一个值在某个区间内出现的长度定义为这个值最后一次出现的位置与第一次出现的位置的差。
解题思路
假设一个数x在区间内出现k次,出现的位置分别为p1、p2一直到pk,那么我们要计入答案的就是pk-p1。
而pk-p1=(pk-pk-1)+(pk-1-pk-2)+…+(p2-p1)。
假设一个数当前在[l,r]区间内,并且它上次出现在位置也在[l,r]区间内,那么可以通过计算这个数当前位置和上次出现的位置的差来累加答案。
因此一个数累加入答案的条件就和它当前的位置和它上一次出现的位置有关,因此可以抽象成一个权值为x-pre[x]的点(pre[x],x),每次查询就相当于查询一个横坐标和纵坐标都在[l,r]范围内矩阵。
又由于pre[x] < x,因此满足条件的点就应该是l<=pre[x] < x<=r,查询就只需要查询横坐标>=l、纵坐标<=r的点的权值之和。
然后就可以用主席树了!然而似乎卡内存……
于是还是用CDQ分治吧……
每次读入修改操作后用set查找更新。
代码
#include <set>
#include <cstdio>
using namespace std;
typedef long long LL;
const int N=1e5+10;
struct Node
{
int x,y,op,val;
Node(int x=0,int y=0,int op=0,int val=0):x(x),y(y),op(op),val(val){}
}C[N<<3],P[N<<3];
set<int>put[N];
int n,m,cnt,pos[N],c[N];
LL ans[N];
void Init()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;++i)
{
scanf("%d",&c[i]);
put[c[i]].insert(i);
if (pos[c[i]]) C[++cnt]=Node(i,pos[c[i]],0,i-pos[c[i]]);
pos[c[i]]=i;
}
}
void Change(int pos,int col)
{
if (c[pos]==col) return ;
//Erase in previous color set
set<int>::iterator it=put[c[pos]].find(pos);
int pre=0,last=0;
it++;
if (it!=put[c[pos]].end())
{
last=*it;
C[++cnt]=Node(last,pos,0,pos-last);
}
it--;
if (it!=put[c[pos]].begin())
{
it--;
pre=*it;
C[++cnt]=Node(pos,pre,0,pre-pos);
it++;
}
if (last && pre) C[++cnt]=Node(last,pre,0,last-pre);
put[c[pos]].erase(pos);
//Insert in now color set
pre=0; last=0;
put[col].insert(pos);
it=put[col].find(pos);
it++;
if (it!=put[col].end())
{
last=*it;
C[++cnt]=Node(last,pos,0,last-pos);
}
it--;
if (it!=put[col].begin())
{
it--;
pre=*it;
C[++cnt]=Node(pos,pre,0,pos-pre);
it++;
}
if (last && pre) C[++cnt]=Node(last,pre,0,pre-last);
//change color
c[pos]=col;
}
struct Bit
{
#define lowbit(x) (x&(-x))
LL x[N];
void Add(int set,int val)
{
for (;set<N;set+=lowbit(set))
x[set]+=val;
}
LL Query(int set)
{
LL ret=0;
for (;set;set-=lowbit(set))
ret+=x[set];
return ret;
}
}bit;
void CDQ(int l,int r)
{
if (l==r) return ;
int mid=(l+r)>>1;
CDQ(l,mid); CDQ(mid+1,r);
for (int i=l;i<=r;++i) P[i]=C[i];
int p1=l,p2=mid+1,top=l;
while (p1<=mid || p2<=r)
{
if (p2>r || (p1<=mid && P[p1].x<=P[p2].x))
{
C[top++]=P[p1];
if (!P[p1].op) bit.Add(n-P[p1].y+1,P[p1].val);
p1++;
}else
{
C[top++]=P[p2];
if (P[p2].op) ans[P[p2].val]+=bit.Query(n-P[p2].y+1);
p2++;
}
}
for (int i=l;i<p1;++i)
if (!P[i].op) bit.Add(n-P[i].y+1,-P[i].val);
}
void Solve()
{
int op,a,b,cntq=0;;
for (int i=1;i<=m;++i)
{
scanf("%d%d%d",&op,&a,&b);
if (op==1) Change(a,b);
else C[++cnt]=Node(b,a,1,++cntq);
}
CDQ(1,cnt);
for (int i=1;i<=cntq;++i) printf("%I64d\n",ans[i]);
}
int main()
{
Init();
Solve();
return 0;
}