「LG6578-魔法少女网站」题解

P6578 [Ynoi2019] 魔法少女网站

sol

前言

操作分块套序列分块,其实思维难度不大吧,只是码量略大看起来不很小清新,以及卡常卡死我了。

无修部分

先考虑一次查询怎么做。

考虑把原序列转化成 01 串,\(\le x\) 的为 1,然后就只需要维护极大连续 1 段的长度信息来计算答案,这是简单的,我们只需要维护每一个连续段端点所在连续段的另一个端点位置即可。具体维护方式可以参考代码实现。

考虑多次询问,把所有查询按 \(x\) 升序排序,那么每次都只会添加一些点。添点操作也是简单的,只会有新增连续段、拓展原有连续段和合并两个连续段三种情况。

带修

一个简单的想法是,对升序排序后的每一次询问,都把所有有影响的修改操作一遍,再全部退回。

先不管这个复杂度肯定假了,我们可以借助序列分块做到 \(O(1)\) 添点或撤回、\(O(\sqrt n)\) 区间查询答案。对于撤回操作,我们存下需要修改的“端点位置”及其“原另一端点信息”以及修改的位置即可。

然后我们考虑操作分块,每 \(\sqrt n\) 次操作分为一块,对一块中的全部查询操作都只需要遍历 \(\sqrt n\) 个修改进行判断,这样复杂度是很对的。

具体地,我们升序遍历当前块的询问,并不撤回地将比当前询问 \(x\) 小的数位置设 \(1\),然后遍历所有块内操作,进行分讨后进行修改操作,这里的修改操作在这一个询问处理结束后需要撤回。具体的分讨过程是简单的,可以直接看代码理解一下。

然后就结束了,经过大力卡常,我的代码最慢点跑了 \(3.48s\) 冲过去了。

code

const int N=3e5+5;

int B1,B2;

int n,m;
int tot;
int a[N];
int L[N],R[N];
ll cal[N];
int sz1,sz2;
struct q1{int t,x,v;}Q1[N];
struct q2{int t,id,l,r,v;}Q2[N],Q3[N];

ll ans[N],sum[N];
int b[N],to[N];
bool vis[N],upd[N];
int ecnt;
int hd[N],ne[N],too[N];
inline void adde(int a,int b){++ecnt,ne[ecnt]=hd[a],too[ecnt]=b,hd[a]=ecnt;}
int top;
struct rmv{ll val;int x,p[2],v[2];}stk[N];
inline void modify(int x,bool frv){
	int bid=(x-1)/B1+1;
	b[x]=1;
	int lst=x-1,nxt=x+1;
	bool fl=(x!=L[bid]&&b[x-1]),fr=(x!=R[bid]&&b[x+1]);
	++top;
	stk[top].x=x;
	if(fl&&fr){
		stk[top].val=1ll*(nxt-to[lst])*(to[nxt]-lst);
		stk[top].p[0]=to[lst],stk[top].v[0]=lst;
		stk[top].p[1]=to[nxt],stk[top].v[1]=nxt;
		lst=to[lst],nxt=to[nxt];
		to[lst]=nxt,to[nxt]=lst;
	}else if(fl){
		stk[top].val=nxt-to[lst];
		stk[top].p[0]=x,stk[top].v[0]=0;
		stk[top].p[1]=to[lst],stk[top].v[1]=lst;
		lst=to[lst],nxt=to[nxt];
		to[lst]=x,to[x]=lst;
	}else if(fr){
		stk[top].val=to[nxt]-lst;
		stk[top].p[0]=x,stk[top].v[0]=0;
		stk[top].p[1]=to[nxt],stk[top].v[1]=nxt;
		lst=to[lst],nxt=to[nxt];
		to[x]=nxt,to[nxt]=x;
	}else{
		stk[top].val=1;
		stk[top].p[0]=stk[top].p[1]=x,stk[top].v[0]=stk[top].v[1]=0;
		to[x]=x;
	}
	sum[bid]+=stk[top].val;
	if(frv)--top;
};
inline ll query(int l,int r){
	ll res=0;
	int cnt=0;
	int lb=(l-1)/B1+1,rb=(r-1)/B1+1;
	if(lb==rb){
		rep(i,l,r)if(b[i])++cnt;else res+=cal[cnt],cnt=0;
		res+=cal[cnt];
	}else{
		rep(i,l,R[lb])if(b[i])++cnt;else res+=cal[cnt],cnt=0;
		rep(i,lb+1,rb-1){
			if(b[L[i]]&&to[L[i]]==R[i])cnt+=R[i]-L[i]+1;
			else{
				if(b[L[i]])cnt+=to[L[i]]-L[i]+1,res-=cal[to[L[i]]-L[i]+1];
				res+=cal[cnt]+sum[i];cnt=0;
				if(b[R[i]])cnt+=R[i]-to[R[i]]+1,res-=cal[R[i]-to[R[i]]+1];
			}
		}
		rep(i,L[rb],r)if(b[i])++cnt;else res+=cal[cnt],cnt=0;
		res+=cal[cnt];
	}
	return res;
};
inline void clear(){
	while(top){
		sum[(stk[top].x-1)/B1+1]-=stk[top].val;
		b[stk[top].x]=0;
		to[stk[top].p[0]]=stk[top].v[0];
		to[stk[top].p[1]]=stk[top].v[1];
		--top;
	}
};
int bcnt[N];
inline void solve(){
	rep(i,1,n)b[i]=0;
	rep(i,1,tot)sum[i]=0;
	ecnt=0;
	rep(i,1,n)hd[i]=0;
	rep(i,1,sz1)vis[Q1[i].x]=1;
	rep(i,1,n)if(!vis[i])adde(a[i],i);
	rep(i,1,sz2)++bcnt[Q2[i].v];
	rep(i,1,n)bcnt[i]+=bcnt[i-1];
	rep(i,1,sz2)Q3[bcnt[Q2[i].v]--]=Q2[i];
	rep(i,1,sz2)Q2[i]=Q3[i];
	rep(i,1,n)bcnt[i]=0;
	int nov=0;
	rep(i,1,sz2){
		while(nov<Q2[i].v){
			++nov;
			for(int e=hd[nov];e;e=ne[e])modify(too[e],1);
		}
		per(j,sz1,1)if(Q1[j].t<Q2[i].t&&!upd[Q1[j].x]){
			upd[Q1[j].x]=1;
			if(Q1[j].v<=Q2[i].v)modify(Q1[j].x,0);
		}
		rep(j,1,sz1)if(!upd[Q1[j].x]){
			upd[Q1[j].x]=1;
			if(a[Q1[j].x]<=Q2[i].v)modify(Q1[j].x,0);
		}
		ans[Q2[i].id]=query(Q2[i].l,Q2[i].r);
		clear();
		rep(j,1,sz1)upd[Q1[j].x]=0;
	}
	rep(i,1,sz1)a[Q1[i].x]=Q1[i].v,vis[Q1[i].x]=0;
	rep(i,1,sz2)write(ans[i],'\n');
}

int main(){
	read(n),read(m);
	B1=sqrt(0.75*n)+1,B2=sqrt(17*m);
	tot=(n-1)/B1+1;
	rep(i,1,tot)L[i]=(i-1)*B1+1,R[i]=min(i*B1,n);
	rep(i,1,n)cal[i]=1ll*i*(i+1)/2;
	rep(i,1,n)read(a[i]);
	while(~m){
		sz1=sz2=0;
		while(sz1+sz2<B2&&m--){
			int o=read();
			if(o==1){
				int x=read(),y=read();
				++sz1,Q1[sz1]={sz1+sz2,x,y};
			}else{
				int l=read(),r=read(),x=read();
				++sz2,Q2[sz2]={sz1+sz2,sz2,l,r,x};
			}
		}
		solve();
	}
	return 0;
}
posted @ 2025-05-31 21:43  LastKismet  阅读(95)  评论(0)    收藏  举报