1. 程式人生 > 實用技巧 >【loj 2736】「JOISC 2016 Day 3」回轉壽司【分塊】

【loj 2736】「JOISC 2016 Day 3」回轉壽司【分塊】

傳送門

Solution

首先考慮如果所有區間都是\([1,n]\),那麼每次我們只需要知道全域性的最大值就行了,只需要維護一個大根堆,並不關心內部的數字的順序。

考慮分塊,然後對整塊做出類似的處理

對每一個塊用一個大根堆維護權值,那麼在詢問時遇到整塊直接取出最大值,如果最大值大於\(A\)就將最大值取出來作為新的\(A\),將\(A\)push進大根堆,同時打一個標記。

對於散塊,我們希望能夠根據塊上的標記,直接暴力復原它的真是面貌:

考慮從左到右直接掃一遍,對於\(a_i\),如果目前所有標記中最小的一個都比\(a_i\)大,那麼\(a_i\)就可以保留,否則就將\(a_i\)替換為最小的標記,將\(a_i\)

作為新的標記
因為標記的順序時互不影響的,所以我們用一個小根堆維護標記即可

總複雜度為\(\mathcal O(Q\sqrt{n}log(n))\),注意暴力跑散塊時要修改維護的大根堆

Code

#include<bits/stdc++.h>
using namespace std;
const int N=4e5+10;
const int S=650;
int n,q,a[N],bel[N],L[N],R[N],bl,k;
priority_queue<int,vector<int>,greater<int> > p[S];
priority_queue<int> v[S];
inline void work(int x){
	if(p[x].empty()) return;
	while(!v[x].empty()) v[x].pop();
	for(int i=L[x];i<=R[x];++i){
		if(p[x].top()<a[i]){
			int u=p[x].top();
			p[x].pop();
			p[x].push(a[i]);
			a[i]=u;	
		}
		v[x].push(a[i]);
	}
	while(!p[x].empty()) p[x].pop();
}
inline void readd(int x){
	while(!v[x].empty()) v[x].pop();
	for(int i=L[x];i<=R[x];++i)
		v[x].push(a[i]);
}
inline int solve(int l,int r,int A){
	int fl=bel[l],fr=bel[r];
	if(fl==fr){
		work(fl);
		for(int j=l;j<=r;++j) if(a[j]>A) swap(a[j],A);
		readd(fl);
	}
	else{
		work(fl);
		for(int j=l;j<=R[fl];++j) if(a[j]>A) swap(a[j],A);
		readd(fl);
		for(int j=fl+1;j<fr;++j)
			if(v[j].top()>A){
				p[j].push(A);v[j].push(A);
				A=v[j].top();v[j].pop();
			}
		work(fr); 
		for(int j=L[fr];j<=r;++j) if(a[j]>A) swap(a[j],A);
		readd(fr);
	}
	return A;
}
int main(){
	scanf("%d%d",&n,&q);
	bl=pow(n,0.5);k=n/bl+1;
	for(int i=1;i<=n;++i)
		scanf("%d",&a[i]),bel[i]=(i-1)/bl+1,v[bel[i]].push(a[i]);
	for(int i=1;i<=k;++i)
		L[i]=(i-1)*bl+1,R[i]=min(i*bl,n);
	for(int i=1,l,r,A;i<=q;++i){
		scanf("%d%d%d",&l,&r,&A);
		if(l<=r) printf("%d\n",solve(l,r,A));
		else{
			A=solve(l,n,A);
			printf("%d\n",solve(1,r,A));
		}

	}
	return 0;
}