Codeforces848C - Goodbye Souvenir[CDQ分治]

博客介绍了CDQ分治相关内容。听人讲解后做例题,将不好求解的式子转化,得出是三维偏序问题,可用CDQ分治解决。每个节点为(t,i,preAi),修改时做相应操作,询问为(t,r,l),用set维护pre,复杂度为O(N(log2N)2)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

神仙CDQ分治%%%

刚听神仙讲完神仙的CDQ分治,顺便做了一道神仙例题

这题,∑i−lastAi\sum i-last_{A_i}ilastAi 显然不好求,考虑转化成

∑i=LRi−preAi[preAi>=L]\sum_{i=L}^{R} i-pre_{A_i} [pre_{A_i}>=L]i=LRipreAi[preAi>=L]

又由于preAi&lt;=ipre_{A_i}&lt;=ipreAi<=i,那么答案也可以直接写成

∑i=1Ri−preAi[preAi&gt;=L]\sum_{i=1}^{R} i-pre_{A_i} [pre_{A_i}&gt;=L]i=1RipreAi[preAi>=L]

∑i−preAi[i&lt;=R][preAi&gt;=L]\sum i-pre_{A_i} [i&lt;=R][pre_{A_i}&gt;=L]ipreAi[i<=R][preAi>=L]

i&lt;=R   preAi&gt;=L  再加上操作的时间轴ti&lt;=R   pre_{A_i}&gt;=L  再加上操作的时间轴ti<=R   preAi>=L  t——

没错,就是三维偏序,CDQ分治!!!


  • 对于每个节点,就为(t,i,preAi)(t,i,pre_{A_i})(t,i,preAi),权值为i−preAii-pre_{A_i}ipreAi
  • 修改的时候,删除只要把上次的抵消掉即可,即加入(t,i,preAi)(t,i,pre_{A_i})(t,i,preAi),权值为−(i−preAi)-(i-pre_{A_i})(ipreAi),然后加入新节点就同上
  • 对于每次询问,就为(t,r,l)(t,r,l)(t,r,l)

至于维护pre,用set即可,复杂度O(N(log2N)2)O(N(log_2^N)^2)O(N(log2N)2)

Code

#include<bits/stdc++.h>
#define gt() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++)
#define pt(ch) (Top<1000000?St[Top++]=ch:(Out(),St[(Top=0)++]=ch))
#define Out() (fwrite(St,1,Top,stdout))
#define LL unsigned int
using namespace std;
int Top;static char St[1000000],buf[1000000],*p1=buf,*p2=buf;
const int maxn=(1e5)+5;
int N,M,T,Q,P[maxn],cnt;LL Ans[maxn],c[maxn];
set<int>S[maxn];
struct ff{
	int x,y,o,f;
	inline int val(){return (~f)?(x-y):(y-x);}
}A[maxn<<3],C[maxn<<3];
int Re(){
	int ret=0;char ch=gt();
	while(ch<'0'||ch>'9') ch=gt();
	while(ch>='0'&&ch<='9') ret=ret*10+(ch^'0'),ch=gt();
	return ret;
}
void Wri(LL x){if(x>9) Wri(x/10);pt(x%10+'0');}
void Add(int i,int x){while(i<=N) c[i]+=x,i+=i&-i;}
int Get(int i){int Res=0;while(i) Res+=c[i],i^=i&-i;return Res;}
void Merge(int L,int mid,int R){
	for(int k=L,i=L,j=mid+1;k<=R;k++) C[k]=(j>R||(i<=mid&&A[i].x<=A[j].x))?A[i++]:A[j++];
	for(int k=L;k<=R;k++) A[k]=C[k];
}
void Solve(int L,int R){
	if(L>=R) return;
	int mid=L+R>>1,i,j;Solve(L,mid),Solve(mid+1,R);
	for(i=mid+1,j=L;i<=R;i++)if(A[i].o){
		while(j<=mid&&A[j].x<=A[i].x){if(!A[j].o) Add(A[j].y,A[j].val());j++;}
		Ans[A[i].o]+=Get(N)-Get(A[i].y-1);
	}
	for(i=L;i<j;i++) if(!A[i].o) Add(A[i].y,-A[i].val());
	Merge(L,mid,R);
}
int main(){
	#ifdef hhh
	freopen("data.in","r",stdin);
	freopen("data.out","w",stdout);
	#endif
	N=Re(),Q=Re();
	for(int i=1;i<=N;i++){
		S[P[i]=Re()].insert(i);
		if(S[P[i]].size()>1) 
		A[++M]=(ff){i,*(--S[P[i]].rbegin()),0,1}; 
	}
	while(Q--){
		int opt=Re(),x=Re(),y=Re();++T;
		if(opt==2){A[++M]=(ff){y,x,++cnt,0};continue;}
		
		auto i=S[P[x]].find(x);int L=0,R=0;
		if(i!=S[P[x]].begin()) A[++M]=(ff){x,L=*--i,0,-1},i++;
		if(++i!=S[P[x]].end()) A[++M]=(ff){R=*i,x,0,-1};
		if(L&&R) A[++M]=(ff){R,L,0,1};
		S[P[x]].erase(x);//Del
		
		S[P[x]=y].insert(x),i=S[y].find(x),L=R=0;
		if(i!=S[y].begin()) A[++M]=(ff){x,L=*--i,0,1},i++;
		if(++i!=S[y].end()) A[++M]=(ff){R=*i,x,0,1};
		if(L&&R) A[++M]=(ff){R,L,0,-1};//Insert
	}
	Solve(1,M);
	for(int i=1;i<=cnt;i++) Wri(Ans[i]),pt('\n');
    return Out(),0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值