[LOJ2736] 「JOISC 2016 Day3」回转寿司(分块+堆)

该博客介绍了如何利用分块和堆数据结构解决一道关于环形区间权值变动的问题。在每次询问中,需要计算经过特定区间后一个值A的变化。通过每个整块使用大根堆维护最大值,以及边界块的暴力重构,结合小根堆处理区间内的值,实现了O(n log n)的时间复杂度解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意

  • 给出一个有 N N N 个点的环,环上各点有一个初始权值 a i a_i ai,给出 Q Q Q个询问,每次询问给出一个区间 [ l , r ] [l,r] [l,r] 和一个值 A A A ,对于 A A A 的变动定义如下( r r r 可能会小于 l l l 因为是环形)。
	for (int i = l; i <= r; i++) if(a[i] > A) swap(a[i], A);
  • 对于每个询问,回答遍历完区间 [ l , r ] [l,r] [l,r] A A A 的最终值。

这个东西没有什么比较好的数据结构可以处理,那么就考虑分块,对于每个整块一个数经过的时候只会改变它的最大值,那么我们对于每个整块用一个大根堆来维护其最大值,边角块的话我们可以暴力重构这个块,区间内经过的值可以用一个小根堆来维护。

因为新的值肯定来自原集合与经过的值,那么每次把经过的值从小到大替换掉大于它的第一个即可,再把新的块构建出来就可以了,这样复杂度是 O ( n n l o g n ) O(n\sqrt{n}logn) O(nn logn)

#include<bits/stdc++.h>
#include<bits/extc++.h>

#define file(s) freopen(s".in", "r", stdin), freopen(s".out", "w", stdout)
#define go(x, i) for(register int i = head[x]; i; i = nxt[i])
#define For(i, a, b) for(register int i = (a), i##_end_ = (b); i <= i##_end_; ++ i)
#define FOR(i, a, b) for(register int i = (a), i##_end_ = (b); i >= i##_end_; -- i)
#define debug(x) cout << #x << " = " << x << endl
#define mem(a, b) memset(a, b, sizeof(a))
#define cpy(a, b) memcpy(a, b, sizeof(a))
#define inf (0x3f3f3f3f)
#define INF (1e18)
#define pb push_back
#define mp make_pair
#define x first
#define y second
#define y1 orzorz

typedef unsigned long long ull;
typedef unsigned int uint;
typedef long long ll;
typedef std::pair<ll, int> PLI;
typedef std::pair<int, int> PII;
typedef long double ldb;
typedef double db;

namespace IO {
#define getc() ((S_ == T_) && (T_ = (S_ = Ch_) + fread(Ch_, 1, Buffsize, stdin), S_ == T_) ? 0 : *S_ ++)
#define putc(x) *nowps ++ = (x)

	const uint Buffsize = 1 << 15, Output = 1 << 23;
	static char Ch_[Buffsize], *S_ = Ch_, *T_ = Ch_;
	static char Out[Output], *nowps = Out;

	inline void flush() {fwrite(Out, 1, nowps - Out, stdout); nowps = Out;}
	template<class T>inline bool chkmax(T &_, T __) {return _ < __ ? _ = __, 1 : 0;}
	template<class T>inline bool chkmin(T &_, T __) {return _ > __ ? _ = __, 1 : 0;}

	template<class T>inline void read(T &_) {
		_ = 0; static char __; T ___ = 1;
		for(__ = getc(); !isdigit(__); __ = getc()) if(__ == '-') ___ = -1;
		for(; isdigit(__); __ = getc()) _ = (_ << 3) + (_ << 1) + (__ ^ 48);
		_ *= ___;
	}

	template<class T>inline void write(T _, char __ = '\n') {
		if(!_) putc('0');
		if(_ < 0) putc('-'), _ = -_;
		static uint sta[111], tp;
		for(tp = 0; _; _ /= 10) sta[++ tp] = _ % 10;
		for(; tp; putc(sta[tp --] ^ 48)); putc(__);
	}

	inline void procStatus() {
		std::ifstream t("/proc/self/status");
		std::cerr << std::string(std::istreambuf_iterator<char>(t), std::istreambuf_iterator<char>());
	}
}

using namespace std;
using namespace IO;

const int N = 4e5 + 10;
const int size = 500;

int st[N], ed[N], a[N], be[N], cnt, n, q;

priority_queue<int, vector<int>, greater<int> > Q[N];
priority_queue<int> h[N];

void clear(priority_queue<int, vector<int>, greater<int> > &ovo) {
	priority_queue<int, vector<int>, greater<int> > qaq; swap(qaq, ovo);
}

void Clear(priority_queue<int> &ovo) {
	priority_queue<int> qaq; swap(qaq, ovo);
}

void rebuild(int bh, int l, int r, int &v) {
	For(i, st[bh], ed[bh]) Q[bh].push(a[i]), a[i] = Q[bh].top(), Q[bh].pop();
	For(i, l, r) if(a[i] > v) swap(a[i], v);
	Clear(h[bh]), clear(Q[bh]);
	For(i, st[bh], ed[bh]) h[bh].push(a[i]);
}

void update(int l, int r, int &v) {
	int L = be[l], R = be[r];
	if(L ^ R) {
		rebuild(L, l, ed[L], v);
		For(i, L + 1, R - 1) {
			Q[i].push(v);
			int k = h[i].top(); h[i].pop();
			if(k > v) swap(k, v);
			h[i].push(k);
		}
		rebuild(R, st[R], r, v);
	}
	else rebuild(L, l, r, v);
}

int main() {
#ifdef ylsakioi
	file("in");
#endif

	int l, r, v;

	read(n), read(q);
	For(i, 1, n) {
		if(i % size == 1) st[++ cnt] = i;
		if(i % size == 0 || i == n) ed[cnt] = i;
		read(a[i]), be[i] = cnt, h[cnt].push(a[i]);
	}

	while(q --) {
		read(l), read(r), read(v);
		if(l <= r) update(l, r, v);
		else update(l, n, v), update(1, r, v);
		write(v);
	}

	return flush(), 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值