[题解] CF848C Goodbye Souvenir
博客内食用效果更佳
思路:CDQ 分治(三维偏序)
对于给出的一个序列
所以对于
显然地,当
现在已经有两维度的条件,加上时间维度便是三维条件,所以可以用三维偏序进行求解。
具体实现:
- 对于
pre 的维护- 采用 set 维护,对于数值
v 来说,我们用s_v 存储所有v 出现的位置。
- 采用 set 维护,对于数值
- 对于初始序列
- 只需要加入每个点的贡献,对于第
i 个位置,将\left(pre_i,i,t\right) 加入 CDQ 中,值为i-pre_i 。
- 只需要加入每个点的贡献,对于第
- 对于修改操作
\left(a,b,c\right) - 将原来的值的贡献减掉:删除
b 对pre_b 的贡献,删除nex_b 对b 的贡献,加入nex_b 对pre_b 的贡献。 - 将
b 从s_{val_b} 中删除,将b 加入s_c ,将val_b 更新为c 。 - 将新的值的贡献加入,与前面的操作类似。
- 将原来的值的贡献减掉:删除
- 对于询问操作
\left(a,b,c\right) - 直接将
\left(b,c,t\right) 加入 CDQ 中。
- 直接将
代码实现需要注意的地方:
- set 的迭代器左闭右开,即
s.end()所指的位置不在序列中。
我的代码采用的是先对
参考代码:
#define LL long long
#define UN unsigned
#include<bits/stdc++.h>
using namespace std;
//--------------------//
const int N=1e5+1;
int n,m;
int val[N];
set<int>s[N];
//--------------------//
struct CT//树状数组
{
LL t[N];
int lobit(int x){return x&-x;}
void change(int p,LL val)
{
while(p<=n)
t[p]+=val,p+=lobit(p);
return;
}
LL query(int p)
{
LL res=0;
while(p>0)
res+=t[p],p-=lobit(p);
return res;
}
}T;
//--------------------//
//CDQ
const int NM=1e6+1;
struct CDQ_Node
{
int l,r,t;
LL val;
int type;
}cdq[NM],tem[NM];
int cnt;
int ast;//ast 是询问操作计数
LL ans[N];
void CDQ(int l,int r)
{
if(l==r)
return;
int mid=l+r>>1,p1=l,p2=mid+1;
CDQ(l,mid);
CDQ(mid+1,r);
for(int i=l;i<=r;i++)
{
if(p1<=mid&&(p2>r||cdq[p1].l>=cdq[p2].l))//按照 l 大小排序
{
if(cdq[p1].type==1)
T.change(cdq[p1].r,cdq[p1].val);
tem[i]=cdq[p1++];
}
else
{
if(cdq[p2].type==2)
ans[cdq[p2].val]+=T.query(cdq[p2].r);
tem[i]=cdq[p2++];
}
}
for(int i=l;i<=mid;i++)//回溯
if(cdq[i].type==1)
T.change(cdq[i].r,-cdq[i].val);
for(int i=l;i<=r;i++)
{
cdq[i]=tem[i];
}
return;
}
//--------------------//
int main()
{
multiset<int>::iterator it;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&val[i]);
s[val[i]].insert(i);
it=s[val[i]].find(i);
if(it!=s[val[i]].begin())//没有前缀则不需要修改
{
--it;
cdq[++cnt]={*it,i,cnt,i-*it,1};
}
}
int a,b,c,last,pre,nex;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&a,&b,&c);
if(a==1)
{
if(val[b]==c)
continue;
pre=0,nex=0;
it=s[val[b]].find(b);
if(it!=s[val[b]].begin())--it,pre=*it,++it;
++it;
if(it!=s[val[b]].end())nex=*it;
//删除原先的贡献
if(pre)cdq[++cnt]={pre,b,cnt,pre-b,1};
if(nex)cdq[++cnt]={b,nex,cnt,b-nex,1};
if(pre&&nex)cdq[++cnt]={pre,nex,cnt,nex-pre,1};
s[val[b]].erase(b);
val[b]=c;
s[c].insert(b);
pre=0,nex=0;
it=s[c].find(b);
if(it!=s[c].begin())--it,pre=*it,++it;
++it;
if(it!=s[c].end())nex=*it;
//添加新的贡献
if(pre)cdq[++cnt]={pre,b,cnt,b-pre,1};
if(nex)cdq[++cnt]={b,nex,cnt,nex-b,1};
if(pre&&nex)cdq[++cnt]={pre,nex,cnt,pre-nex,1};
}
else
cdq[++cnt]={b,c,cnt,++ast,2};
}
CDQ(1,cnt);
for(int i=1;i<=ast;i++)
printf("%lld\n",ans[i]);
return 0;
}