[Ynoi2016] 镜中的昆虫

IV.IV.[Ynoi2016] 镜中的昆虫

没错,这里就是CDQ分治 \(O(n)\) 的优势所在了——本题似乎卡掉了空间复杂度为 \(O(n\log^2n)\) 的树套树。

但这不妨碍我继续说:树套树yyds

首先,这里有一个结论:长度为 \(n\) 的序列,修改 \(m\) 次,\(las\) 数组变化的次数是 \(O(n+m)\) 的。

证明:

我们不妨将其看成初始有一个长度为 \(n\) 的全相同序列,之后先进行 \(n\) 次单点修改,再进行 \(m\) 次区间修改,也就是说总共进行 \(n+m\) 次修改。

考虑一次修改 \([l,r]\)。显然,此次修改相当于把 \(las_l\) 设成一个可以被求出的值,然后将 \(i\in(l,r]\)\(las_i\) 设作 \(i-1\)

同时,其会影响 \([l,r]\) 中出现过的所有颜色,再加上区间修改的目标颜色,这所有颜色的各一个位置——但是这些位置必定是以前某个 \([l,r]\)\(l\) 位置,或者是被某次修改打断形成的新区间。

实际上,这里可以结合珂朵莉树的思想理解——珂朵莉树中存了连续的取值相同的区间,而此种区间除了开头的一个数的 \(las\) 值外,其余 \(las\) 都是 \(i-1\)

一次修改,会产生 \(O(1)\) 个新区间,并推平一些旧区间。但是旧区间除了区间开头的元素,其余位置都不需要改变。因为一个区间最多产生一次并被推平一次,所以总数是 \(O(n+m)\) 级别的。

这时直接用珂朵莉树维护 \(las\) 值。要开一棵大珂朵莉树,同时对每种颜色各开一棵小珂朵莉树。

时间复杂度 \(O(n\log^2n)\)

珂朵莉树太难写了,一坨坨 set 看得人头晕

代码:

#include<bits/stdc++.h>
using namespace std;
int n,m,a[100010],las[100010],res[100010],t[100010],buc[200100];
vector<int>v;
struct node{
	int tp,a,b;
	node(){}
	node(int T,int A,int B){tp=T,a=A,b=B;}
	friend bool operator<(const node&x,const node&y){return x.a==y.a?!x.tp>!y.tp:x.a<y.a;}
};
void ADD(int x,int y){while(x<=n)t[x]+=y,x+=x&-x;}
int SUM(int x){int ret=0;while(x)ret+=t[x],x-=x&-x;return ret;}
vector<node>u,w[100010]; 
int L[100100],R[100100],C[100100],tp[100100];
set<pair<int,int> >s[200010];
struct dat{
	int l,r,c;
	dat(int L,int R,int C){l=L,r=R,c=C;}
	friend bool operator<(const dat&u,const dat&v){return u.l<v.l;}
};
set<dat>S;
void CDQ(int l,int r){
	if(l==r)return;
	int mid=(l+r)>>1;
	for(int i=l;i<=mid;i++)if(!tp[i])for(auto j:w[i])u.push_back(j);
	for(int i=mid+1;i<=r;i++)if(tp[i])for(auto j:w[i])u.push_back(j);
	sort(u.begin(),u.end());
	for(auto i:u){
		if(!i.tp){
			if(i.b>0)ADD(i.b,1);
			else ADD(-i.b,-1);
		}else{
			if(i.tp>0)res[i.tp]+=SUM(i.b);
			else res[-i.tp]-=SUM(i.b);
		}
	}
	u.clear();
	for(auto i:u)if(!i.tp){
		if(i.b>0)ADD(i.b,-1);
		else ADD(-i.b,1);
	}
	CDQ(l,mid),CDQ(mid+1,r);
}
#define CLAS(x,y) w[i].emplace_back(0,x,-las[x]),w[i].emplace_back(0,x,las[x]=y)
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]),v.push_back(a[i]);
	for(int i=1;i<=m;i++){
		scanf("%d%d%d",&tp[i],&L[i],&R[i]),tp[i]=(tp[i]==2);
		if(!tp[i])scanf("%d",&C[i]),v.push_back(C[i]);
	}
	sort(v.begin(),v.end()),v.resize(unique(v.begin(),v.end())-v.begin());
	for(int i=1;i<=n;i++)a[i]=lower_bound(v.begin(),v.end(),a[i])-v.begin()+1;
	for(int i=1;i<=m;i++)if(!tp[i])C[i]=lower_bound(v.begin(),v.end(),C[i])-v.begin()+1;
	for(int i=1;i<=n;i++)las[i]=buc[a[i]]+1,buc[a[i]]=i;
	for(int i=1;i<=m;i++){
		if(!tp[i])continue;
		u.emplace_back(-i,L[i]-1,L[i]),u.emplace_back(i,R[i],L[i]);
		w[i].emplace_back(-i,L[i]-1,L[i]),w[i].emplace_back(i,R[i],L[i]);
	}
	sort(u.begin(),u.end());
	for(int i=0,j=1;i<u.size();i++){
		while(j<=u[i].a)ADD(las[j++],1);
		if(u[i].tp>0)res[u[i].tp]+=SUM(u[i].b);else res[-u[i].tp]-=SUM(u[i].b);
	}
	u.clear(),memset(t,0,sizeof(t));
	
	for(int i=1,j=1;i<=n;i=j){
		while(j<=n&&a[i]==a[j])j++;
		S.insert(dat(i,j-1,a[i]));
		s[a[i]].insert(make_pair(i,j-1));
	}
	for(int i=1;i<=m;i++){
		if(tp[i])continue;
		auto p=S.upper_bound(dat(L[i],L[i],L[i])),q=S.upper_bound(dat(R[i],R[i],R[i]));
		auto r=p--;
		for(;r!=q;++r)CLAS(r->l,r->l);
		
		for(r=p;r!=q;++r)s[r->c].erase(make_pair(r->l,r->r));
		if(p->l<L[i])s[p->c].insert(make_pair(p->l,L[i]-1));
		r=q;--r;
		if(r->r>R[i])s[r->c].insert(make_pair(R[i]+1,r->r));
		s[C[i]].insert(make_pair(L[i],R[i]));
		auto o=s[C[i]].lower_bound(make_pair(L[i],0));
		if(o!=s[C[i]].begin())--o,CLAS(L[i],o->second+1);else CLAS(L[i],1);
		for(r=p;r!=q;++r){
			o=s[r->c].upper_bound(make_pair(R[i],0x3f3f3f3f));
			if(o==s[r->c].end())continue;
			int tmp=o->first;
			if(o!=s[r->c].begin())--o,CLAS(tmp,o->second+1);else CLAS(tmp,1);
		}
		o=s[C[i]].upper_bound(make_pair(R[i],0x3f3f3f3f));
		if(o!=s[C[i]].end())CLAS(o->first,R[i]+1);
		int LL=p->l,LC=p->c;
		r=q;--r;int RR=r->r,RC=r->c;
		for(r=p;r!=q;r=S.erase(r));
		S.insert(dat(L[i],R[i],C[i]));
		if(LL!=L[i])S.insert(dat(LL,L[i]-1,LC));
		if(RR!=R[i])S.insert(dat(R[i]+1,RR,RC));
//		for(auto k:w[i])printf("[%d,%d]",k.a,k.b);puts("");
//		for(auto k:S)printf("(%d,%d,%d)",k.l,k.r,k.c);puts("");
	}
	
	CDQ(1,m);
	for(int i=1;i<=m;i++)if(tp[i])printf("%d\n",res[i]);
	return 0;
}

原文地址:https://www.cnblogs.com/Troverld/p/14620806.html