CF848C Goodbye Souvenir(set+CDQ分治)

洛谷题目传送门

解题思路

我们设 p r e i pre_i prei表示, i i i这个位置上一个出现相同数字的位置, v a l i val_i vali表示 i − p r e i i-pre_i iprei
那我们发现 v a l i + v a l p r e i = i − p r e i + p r e i − p r e p r e i = i − p r e p r e i val_i+val_{pre_i}=i-pre_i+pre_i-pre_{pre_i}=i-pre_{pre_i} vali+valprei=iprei+preipreprei=ipreprei
正好是第一次和最后一次出现位置的位置差,也就是题目求的值
所以我们实际上试求 ∑ i , i ≤ r , p r e i ≥ l v a l i \sum_{i,i\leq r,pre_i\geq l}val_i i,ir,preilvali
这是一个二维偏序的形式,直接用CDQ分治求出来
对于修改操作,可以把原来的贡献减掉,新的贡献加上
可以对每个数字开个set维护位置
因为有了时间顺序,所以实际是
∑ i , i ≤ r , p r e i ≥ l , t i < j v a l i \sum_{i,i\leq r,pre_i\geq l,t_i<j}val_i i,ir,preil,ti<jvali
其中 j j j是当前操作的时间, t i t_i ti是第 i i i操作的时间

#include<bits/stdc++.h>
using namespace std;
const int N = 6e5+7;
typedef long long LL;
const int INF = 1e9+7;
struct node
{
	int x,y,t,v,id,tp;
}q[3*N],tmp[2*N];
int tot=0;
LL ans[N];
LL tree[N];
bool cmp(node a,node b)
{
	if(a.x!=b.x) return a.x<b.x;
	if(a.y!=b.y) return a.y<b.y;
	if(a.t!=b.t) return a.t<b.t;
	return a.tp>b.tp;
}
void Add(int x,int y,int t,int v,int id,int tp)
{
	tot++;
	q[tot].x=x;
	q[tot].y=y;
	q[tot].t=t;
	q[tot].v=v;
	q[tot].id=id;
	q[tot].tp=tp;
}
void add(int x,LL v)
{
	for(int i=x;i<=tot;i+=(i&-i))
	tree[i]+=v;
}
LL ask(int x)
{
	LL res=0;
	for(int i=x;i;i-=(i&-i))
	res+=tree[i];
	return res;
}
void CDQ(int l,int r)
{
	if(l>=r) return;
	int mid=(l+r)>>1;
	CDQ(l,mid);
	CDQ(mid+1,r);
	int i=l,j=mid+1,top=0;
	while(i<=mid&&j<=r)
	{
		if(q[i].y<=q[j].y)
		{
			if(q[i].tp==1) add(q[i].t,q[i].v);
			tmp[++top]=q[i++];
		}
		else
		{
			if(q[j].tp==0) ans[q[j].id]+=ask(q[j].t-1);
			tmp[++top]=q[j++];
		}
	}
	while(i<=mid) 
	{
		if(q[i].tp==1) add(q[i].t,q[i].v);
		tmp[++top]=q[i++];
	}
	while(j<=r) 
	{	
		if(q[j].tp==0) ans[q[j].id]+=ask(q[j].t-1);
		tmp[++top]=q[j++];
	}
	for(int k=l;k<=mid;k++)
	if(q[k].tp==1) add(q[k].t,-q[k].v);
	for(int k=1;k<=top;k++)
	q[l+k-1]=tmp[k];
}
set<int> col[N];
set<int>::iterator it,p;
int n,m;
int c[N];
vector<int> seq;
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&c[i]);
		int pre=0;
		if(col[c[i]].size())
		{
			it=col[c[i]].end();
			it--;
			pre=(*it);
		}	
		col[c[i]].insert(i);
		Add(i,pre?-pre:-INF,1,pre?(i-pre):0,0,1);
	}
	for(int i=1;i<=m;i++)
	{
		int t,a,b;
		scanf("%d %d %d",&t,&a,&b);
		if(t==1)
		{
			int pre=0,nxt=0;
			it=col[c[a]].find(a);
			p=it;
			if(it==col[c[a]].begin()) pre=0;
			else
			{
				it--;
				pre=(*it);
				it++;
			}
			Add(a,pre?-pre:-INF,i+1,pre?-(a-pre):0,0,1);
			it++;
			if(it==col[c[a]].end()) nxt=-1;
			else nxt=a;
			if(nxt!=-1) 
			{
				Add(*it,-nxt,i+1,-((*it)-nxt),0,1);
				Add(*it,pre?-pre:-INF,i+1,pre?(*it)-pre:0,0,1);
			}
			col[c[a]].erase(p);
			c[a]=b;
			col[c[a]].insert(a);
			it=col[c[a]].find(a);
			p=it;
			if(it==col[c[a]].begin()) pre=0;
			else 
			{
				it--;
				pre=(*it);
				it++;
			}
			Add(a,pre?-pre:-INF,i+1,pre?a-pre:0,0,1);
			it++;
			if(it==col[c[a]].end()) nxt=-1;
			else nxt=a;
			if(nxt!=-1) 
			{
				Add(*it,pre?-pre:-INF,i+1,pre?-((*it)-pre):0,0,1);
				Add(*it,-nxt,i+1,(*it)-nxt,0,1);
			}
		}
		else
		{
			Add(b,-a,i+1,0,i,0);
			seq.push_back(i);
		}
	}
	sort(q+1,q+tot+1,cmp);
	CDQ(1,tot);
	for(int i=0;i<seq.size();i++)
	{
		int x=seq[i];
		printf("%lld\n",ans[x]);
	}
	return 0;
}
/*
7 3
1 2 3 1 3 2 1
1 7 2
1 3 2
2 1 6
*/ 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值