【题解】「JOISC 2016 Day 3」回转寿司

想一下传送带 (233)

我记得我和 lh 说了可以对于一个整块把一堆操作放在一起处理

暴力匹配是 O ( n q ) O(nq) O(nq) 的。

但是如果加一个优先队列呢?

那你就可以在 O ( B l o g Q i ) O(BlogQ_i) O(BlogQi) 的时间内复原这个块 (其中 Q i Q_i Qi 是对这个整块操作的次数)

你再算一下重构的次数和每次操作 insert 的那些点不就完了嘛。

#include<bits/stdc++.h>
#define db double 
#define ll long long
#define mkp make_pair
#define pii pair<int,int> 
#define inf 0x3f3f3f3f
#define fi first
#define se second
using namespace std;
const int Maxn=4e5+5;
const int Maxm=630;
const int mod=23333333;
inline int read() {
	int x=0,f=1; char c=getchar();
	while(c<'0'||c>'9') {
		c=getchar();
	}
	while(c>='0'&&c<='9') {
		x=(x<<1)+(x<<3)+c-'0';
		c=getchar();
	}
	return x;
}
int n,block,m,L[Maxn],R[Maxn],a[Maxn],bl[Maxn];
priority_queue<int> q[Maxn/Maxm+5];
priority_queue<int,vector<int>,greater<int>> q2[Maxn/Maxm+5];
void merge(int l) {
	while(q[l].size()) q[l].pop();
	for(int i=L[l];i<=R[l];i++) {
		q[l].push(a[i]);
	}
}
//人去选菜 (选最小的那个) 
void split(int l) {
	for(int i=L[l];i<=R[l];i++) {
		if(q2[l].size()) {
			int tmp=q2[l].top();
			if(tmp<a[i]) {
				q2[l].pop();
				q2[l].push(a[i]);
				a[i]=tmp;
			}
		}
	}
	while(q2[l].size()) q2[l].pop();
	merge(l);
}
int solve(int l,int r,int x) {
	int le=bl[l],ri=bl[r];
	if(le==ri) {
		split(le);
		for(int i=l;i<=r;i++) {
			if(a[i]>x) {
				swap(a[i],x);
			}
		}
		merge(le);
	}
	else {
		split(le);
		split(ri);
		for(int i=l;i<=R[le];i++) {
			if(a[i]>x) {
				swap(a[i],x);
			}
		}
		//来康一康怎么给整块打标记  
		for(int i=le+1;i<=ri-1;i++) {
			int tmp=q[i].top();
			if(tmp>x) {
				q[i].pop();
				q[i].push(x);
				q2[i].push(x);
				x=tmp;
			}
		}
		for(int i=L[ri];i<=r;i++) {
			if(a[i]>x) {
				swap(a[i],x);
			}
		}
		merge(le);
		merge(ri);
	}
	return x;
}
int main() {
//	freopen("data.in","r",stdin);
	n=read(),m=read();
	block=sqrt(n);
	for(int i=1;i<=n;i++) {
		bl[i]=(i-1)/block+1;
	} 
	for(int i=1;i<=n;i++) a[i]=read();
	for(int i=1;i<=(n-1)/block+1;i++) {
		L[i]=(i-1)*block+1;
		R[i]=min(n,i*block);
		merge(i);
	}
//	for(int i=1;i<=(n-1)/block+1;i++) {
//		printf("%d\n",q[i].size());
//	}
	
	for(int i=1;i<=m;i++) {
		int l=read(),r=read(),x=read();
		if(l<=r) {
			printf("%d\n",solve(l,r,x)); 
		}
		else {
			printf("%d\n",solve(1,r,solve(l,n,x)));
		}
//		for(int j=1;j<=(n-1)/block+1;j++) {
//			split(j);
//		}
//		for(int j=1;j<=n;j++) {
//			printf("%d ",a[j]);
//		}
//		printf("\n");
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值