LGP5386题解

写在前面的废话

自己写了两天,调了半天,然后jzp来帮忙调了一个小时,终于过了

过的时候耳机里放着桐姥爷的bgm,就差哭出来了

题解

首先这题没有部分分差评(

  1. 值域不变

我们可以注意到,如果一个区间全部都在值域内(长度为 \(len\)),那么这个区间的答案是 \(\frac {len \times (len+1)} 2\)

然后我们很快能发现一个区间的答案就是这样的区间的答案之和。

然后我们就能用线段树做到 \(O(\log n)\)

  1. 区间不变

此时我们在值域上进行莫队。

记录每个值对应的区间下标,然后可以视为这样的一个问题:

  1. 加边
  2. 询问所有联通块的贡献之和

我们当然能够使用可撤销并查集做到 \(O(m\sqrt n\log n)\)

但仔细想想,把莫队改为回滚莫队可以使用普通并查集做到 \(O(m\sqrt n\alpha(n))\)

  1. 原问题

对序列分块,在莫队时维护块内答案和并查集即可。

整块和散块之间的答案使用算法 \(1\) 的方法即可求解。

复杂度 \(O(m\sqrt n\alpha(n))\),但是因为我并查集挂掉了所以只加了一个优化(

不过这题数据好像没卡,\(O(m\sqrt n\log n)\) 过去了。

附上 AC 前的最后一版代码:

#include<algorithm>
#include<cstdio>
const int M=2e5+5,p=448,SIZ=p+5;
int n,m,len,a[M];long long ans[M];
int L[SIZ],R[SIZ],id[M],pos[M];
int top,valp[M],vald[M],vals[M];
bool vs[SIZ],vis[SIZ][SIZ];int t,pv[M],as[M];
struct Query{
	int L,R,x,y,p,id;
	inline bool operator<(const Query&it)const{
		return p==it.p?y<it.y:x<it.x;
	}
}q[M];
struct data{
	int L,R,len;long long ans;
	inline data operator*(const data&it)const{
		return (data){L+(L==len)*it.L,(it.R==it.len)*R+it.R,len+it.len,ans+it.ans+R*it.L};
	}
	inline void AddL(const bool&val){
		if(val){
			ans+=++L;if(R==len)++R;
		}
		else L=0;++len;
	}
	inline void AddR(const bool&val){
		if(val){
			ans+=++R;if(L==len)++L;
		}
		else R=0;++len;
	}
};
struct Block{
	int ans,len,f[SIZ],siz[SIZ];bool v[SIZ];
	inline void init(){
		for(register int i=0;i<=len;++i)siz[f[i]=i]=1,v[i]=false;ans=0;
	}
	inline void merge(const int&u,const int&v){
		if(siz[u]>siz[v])siz[f[v]=u]+=siz[v];
		else siz[f[u]=v]+=siz[u];
	}
	inline int Find(const int&u){
		return f[u]==u?u:f[u]=Find(f[u]);
	}
	inline void Add(const int&id){
		ans+=siz[Find(id-1)]*siz[Find(id)];merge(Find(id-1),Find(id));v[id]=true;
	}
	inline data Q(){
		return (data){siz[Find(0)]-1,siz[Find(len)]-1,len,ans};
	}
}B[SIZ];
inline void Update(const int&bid,const int&pos){
	if(vis[pos][bid])return;vis[pos][bid]=true;
	if(!vs[pos])++t,vs[pos]=true,as[t]=B[pv[t]=pos].ans;
	++top;vals[top]=B[valp[top]=pos].siz[vald[top]=bid];
}
inline void Modify(const int&l,const int&r){
	register int i,p;
	for(i=l;i<=r;++i){
		p=pos[a[i]];Update(B[p].Find(id[a[i]]),p);Update(B[p].Find(id[a[i]]-1),p);
	}
	for(i=l;i<=r;++i)B[pos[a[i]]].Add(id[a[i]]);
}
inline void clear(const int&L,const int&R){
	for(register int i=L;i<=R;++i)B[pos[a[i]]].v[id[a[i]]]=false;
}
inline void Add(const int&val){
	B[pos[val]].Add(id[val]);
}
inline long long Q(const int&l,const int&r){
	const int&q=pos[l],&p=pos[r];
	register int i;data ans=(data){0,0,0,0};
	if(q==p){
		for(i=l;i<=r;++i)ans.AddR(B[q].v[id[i]]);
	}
	else{
		for(i=q+1;i<p;++i)ans=ans*B[i].Q();
		for(i=R[q];i>=l;--i)ans.AddL(B[q].v[id[i]]);
		for(i=L[p];i<=r;++i)ans.AddR(B[p].v[id[i]]);
	}
	return ans.ans;
}
inline void init(){
	register int i,x;
	scanf("%d%d",&n,&m);len=(n+p-1)/p;
	for(i=1;i<=n;++i){
		scanf("%d",&x);a[x]=i;
		pos[i]=(i-1)/p+1;id[i]=(i-1)%p+1;
	}
	for(i=1;i<=len;++i){
		L[i]=R[i-1]+1;R[i]=p*i;
		if(i==len)R[i]=n;B[i].len=R[i]-L[i]+1;
	}
	for(i=1;i<=m;++i){
		scanf("%d%d%d%d",&q[i].L,&q[i].R,&q[i].x,&q[i].y);
		q[i].p=pos[q[i].x];q[i].id=i;
	}
}
inline void Mo_queue(){
	register int i,j,b,d,id;
	std::sort(q+1,q+m+1);
	for(i=1;i<=m;++i){
		const int&QL=q[i].x,&QR=q[i].y;
		if(i==1||q[i].p!=q[i-1].p){
			for(j=1;j<=len;++j)B[j].init();id=q[i].p*p;
		}
		if(pos[QL]==pos[QR])Modify(QL,QR);
		else{
			while(id<QR)Add(a[++id]);
			Modify(QL,q[i].p*p);
		}
		ans[q[i].id]=Q(q[i].L,q[i].R);
		do{
			b=valp[top];d=vald[top];
			B[b].f[d]=d;B[b].siz[d]=vals[top];vis[b][d]=false;
		}while(--top);
		do B[pv[t]].ans=as[t],vs[pv[t]]=false;while(--t);
		if(pos[QL]==pos[QR])clear(QL,QR);else clear(QL,q[i].p*p);
	}
}
signed main(){
	init();Mo_queue();
	for(register int i=1;i<=m;++i)printf("%lld\n",ans[i]);
}
posted @ 2022-01-10 16:16  Prean  阅读(16)  评论(0编辑  收藏  举报
var canShowAdsense=function(){return !!0};