• [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;
    }
    

  • 相关阅读:
    正则表达式30分钟入门教程
    解读C#中的正则表达式1
    web开发技巧经验积累
    常用效果的实现(Javascript的子父页访问、函数调用)
    document.execCommand()编程
    把任意文件隐藏在一张图片里
    在C#中如何实现Form与Form之间的通信
    全国各个省市数据库
    对长时间装载的ASP.NET页如何在客户端浏览器中显进度
    关于UPC E条码的校验
  • 原文地址:https://www.cnblogs.com/Troverld/p/14620806.html
Copyright © 2020-2023  润新知