[Ynoi2016] 镜中的昆虫 题解

难度在最近遇到的题里相对较高,在这里写一篇珂学题解。


(以下是学校给的部分分)

\(20\%\):直接暴力枚举。

另外 \(20\%\):假如我们取 \(pre\),对于 \(pre<l\) 的,\(ans++\),明显二维偏序,树状数组或 \(cdq\) 即可,时间复杂度 \(O(n\log n)\)

另外 \(40\%\):相当于多加一个时间维,三维偏序,\(cdq\) 典中典,时间复杂度 \(O(n\log^2n)\)

这是正解向的,实际上第二个部分分还可以莫队,第三个部分分还可以分块或带修莫队。


\(100\%\):这里引入珂朵莉树:

模板题 \(CF896C\),也是普遍认为的算法来源。

实际上就是将相同的数合到一个区间,对于区间赋值(珂朵莉树中通常称为推平),直接将这些区间全部并到一块,合成一个区间(珂朵莉树精髓,\(assign\))。时间复杂度在随机数据下,可证明为 \(O(n\log n)\) 级别的,可以自己上网搜。

我们用珂朵莉树维护 \(pre\),就可以保证时间维变成 \(n+m\) 个。

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

//中国珂学院 SNGXYZ 分院 OI 科第三办公室研究员 LYH
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2e5+5,M=2e6+5;
int n,m,cnm,cnq,pr[N];
int a[N],lst[N],ans[N],cna;
struct node{
	int x,y,t,v,id;
}q[M];map<int,int>mp;
int cmp1(node x,node y){
	return x.t!=y.t?x.t<y.t:x.id<y.id;
}int cmp2(node x,node y){
	return x.x!=y.x?x.x<y.x:x.id<y.id;
}struct chtholly{
	struct odt{
		int l,r;
		mutable int v;
		bool operator<(const odt &c)const{
			return l<c.l;
		}
	};set<odt>st,cl[N];
	#define iter set<odt>::iterator
	iter ins(int l,int r,int v){
		cl[v].insert({l,r,v});
		return st.insert({l,r,v}).first;
	}void del(int l,int r,int v){
		cl[v].erase({l,r,v});
		st.erase({l,r,v});
	}iter spilt(int x){
		iter it=st.lower_bound({x,0,0});
		if(it!=st.end()&&(*it).l==x) return it;
		it--;int l=(*it).l,r=(*it).r,v=(*it).v;
		del(l,r,v),ins(l,x-1,v);
		return ins(x,r,v);
	}int pre(int x){
		iter it=--st.upper_bound({x,0,0});
		if((*it).l<x) return x-1;
		iter ch=cl[(*it).v].lower_bound({x,0,0});
		return (ch!=cl[(*it).v].begin())*(*(--ch)).r;
	}void assign(int l,int r,int v,int t){
		iter tr=spilt(r+1),tl=spilt(l);
		vector<int>ps;
		for(iter it=tl;it!=tr;it++){
			if(it!=tl) ps.emplace_back((*it).l);
			iter nxt=cl[(*it).v].upper_bound(*it);
			if(nxt!=cl[(*it).v].end()) ps.emplace_back((*nxt).l);
			cl[(*it).v].erase(*it);
		}st.erase(tl,tr);ins(l,r,v);
		ps.emplace_back(l);
		iter nxt=cl[v].upper_bound({l,r,v});
		if(nxt!=cl[v].end()) ps.emplace_back((*nxt).l);
		for(int i=0;i<ps.size();i++){
			q[++cnq]=(node){ps[i],pr[ps[i]],t,-1,0};
			pr[ps[i]]=pre(ps[i]);
			q[++cnq]=(node){ps[i],pr[ps[i]],t,1,0};
		}
	}
}seniorious;
struct BIT{
	int c[N];
	void add(int x,int y){
		for(;x<N;x+=x&-x)
			c[x]+=y;
	}int ans(int x){
		int re=0;
		for(;x;x-=x&-x)
			re+=c[x];
		return re;
	}
}bit;
void cdq(int l,int r){
	if(l==r) return;
	int mid=(l+r)/2;
	cdq(l,mid),cdq(mid+1,r);
	int i=l,j=mid+1;
	while(j<=r){
		while(i<=mid&&q[i].x<=q[j].x){
			if(!q[i].id) bit.add(q[i].y+1,q[i].v);
			i++;
		}if(q[j].id) ans[q[j].id]+=bit.ans(q[j].y+1)*q[j].v;
		j++;
	}for(int k=l;k<i;k++)
		if(!q[k].id) bit.add(q[k].y+1,-q[k].v);
	inplace_merge(q+l,q+mid+1,q+r+1,cmp2);
}signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		if(!mp[a[i]])
			mp[a[i]]=++cnm;
		a[i]=mp[a[i]];
		pr[i]=lst[a[i]];
		lst[a[i]]=i;
		q[++cnq]={i,pr[i],0,1,0};
		seniorious.ins(i,i,a[i]);
	}for(int i=1;i<=m;i++){
		int x;cin>>x;
		if(x==1){
			int l,r,v;cin>>l>>r>>v;
			if(!mp[v]) mp[v]=++cnm;
			seniorious.assign(l,r,mp[v],i);
		}else{
			int l,r;cin>>l>>r;
			q[++cnq]={r,l-1,i,1,++cna};
			q[++cnq]={l-1,l-1,i,-1,cna};
		}
	}sort(q+1,q+cnq+1,cmp1);
	cdq(1,cnq);
	for(int i=1;i<=cna;i++)
		cout<<ans[i]<<"\n";
	return 0;
}/*
在太阳西斜的这个世界里,置身天上之森。
等这场战争结束之后,不归之人与望眼欲穿的众人。
人人本着正义之名,长存不灭的过去、逐渐消逝的未来。
我回来了,纵使日薄西山,即便看不到未来。
此时此刻的光辉,盼君勿忘。
————世界上最幸福的女孩
*/
posted @ 2024-08-15 20:22  长安一片月_22  阅读(32)  评论(1编辑  收藏  举报