[洛谷P4688][Ynoi2016]掉进兔子洞

本文介绍了一种解决区间交集与并集问题的方法,使用bitset和离散化技巧,通过莫队算法优化查询效率,特别适用于大规模数据集。

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

题目大意:给定一个$n(n\leqslant10^5)$序列,$m(m\leqslant10^5)$个询问,每个询问给出$l_1,r_1,l_2,r_2,l_3,r_3$。令$s$为该三个区间的交集的大小,则输出$|[l_1,r_1]|+|[l_2,r_2]|+|[l_3,r_3]|−3|s|$

题解:$|[l_1,r_1]|+|[l_2,r_2]|+|[l_3,r_3]|$这一部分比较好求,主要就是求$|s|$,$s$是这三个区间元素的并集,可以想到用$bitset$,但是$bitset$似乎只可以求有多少种相同元素,而不可以求有多少个相同元素,这时可以改一下离散化的方式,排序后不要去重,这时就可以用这个数和这个数现在已经出现的次数定下一个唯一确定位置。这样就可以完成求并集的过程了。

这里可以用莫队来求每个数出现次数以及那一个元素出现的集合。但是发现空间复杂度是$O(\dfrac{nm}{\omega})$,开不下。可以把询问分成$3$次进行处理,就可以了

卡点:把一个$maxm$打成了$maxn$,然后$RE$

 

C++ Code:

#include <algorithm>
#include <bitset>
#include <cstdio>
#include <cctype>
#include <iostream>
namespace __IO {
	namespace R {
		int x, ch;
		inline int read() {
			while (isspace(ch = getchar()));
			for (x = ch & 15; isdigit(ch = getchar()); ) x = x * 10 + (ch & 15);
			return x;
		}
	}
}
using __IO::R::read;

#define maxn 100010
#define maxm 35000

int n, m;
int s[maxn], v[maxn];
std::bitset<maxn> ans[maxm + 10], res;
struct Query {
	int l, r, id;
	inline friend bool operator < (const Query &lhs, const Query &rhs) {
		return lhs.l >> 8 == rhs.l >> 8 ? (lhs.l >> 8 & 1 ? lhs.r > rhs.r : lhs.r < rhs.r) : lhs.l < rhs.l;
	}
} q[maxm * 3 + 10];

int tmpans[maxm + 10], cnt[maxn];

inline void add(int x) {res.set(x + cnt[x]); cnt[x]++;}
inline void del(int x) {cnt[x]--; res.reset(x + cnt[x]);}

void solve() {
	int tot = 0;
	for (int i = 1; m && i < maxm; i++, m--) {
		ans[i].set(); tmpans[i] = 0;
		q[++tot].id = i, q[tot].l = read(), q[tot].r = read(), tmpans[i] += q[tot].r - q[tot].l + 1;
		q[++tot].id = i, q[tot].l = read(), q[tot].r = read(), tmpans[i] += q[tot].r - q[tot].l + 1;
		q[++tot].id = i, q[tot].l = read(), q[tot].r = read(), tmpans[i] += q[tot].r - q[tot].l + 1;
	}
	res.reset();
	for (int i = 1; i <= n; i++) cnt[i] = 0;
	int l = 1, r = 0;
	std::sort(q + 1, q + tot + 1);
	for (int i = 1; i <= tot; i++) {
		while (r < q[i].r) add(s[++r]);
		while (l > q[i].l) add(s[--l]);
		while (r > q[i].r) del(s[r--]);
		while (l < q[i].l) del(s[l++]);
		ans[q[i].id] &= res;
	}
	const int M = tot / 3;
	for (int i = 1; i <= M; i++) printf("%d\n", tmpans[i] - ans[i].count() * 3);
}

int main() {
	n = read(), m = read();
	for (int i = 1; i <= n; i++) v[i] = s[i] = read();
	std::sort(v + 1, v + n + 1);
	for (int i = 1; i <= n; i++) s[i] = std::lower_bound(v + 1, v + n + 1, s[i]) - v;
	while (m) solve();
	return 0;
}

  

转载于:https://www.cnblogs.com/Memory-of-winter/p/10120966.html

题目 P5413 "YNOI2019 骑单车" 是一个经典的动态规划和贪心算法的问题。该题主要涉及两个概念:路径规划和状态转移方程。 **背景描述** 假设你在一个二维网格上,每个单元格代表一个地点,你需要从起点出发骑车到终点,并尽可能地减少骑行时间。网格中的每个单元格都有两种可能的状态:平地(速度不变)或斜坡(速度减半)。你的目标是找到一条最短的路线。 **关键点解析** 1. **动态规划**:通常用于求解最优化问题。在这个问题中,我们可以定义一个二维数组 dp[i][j] 表示从起点到位置 (i, j) 的最短行驶时间。状态转移方程会根据当前位置的性质(平地还是斜坡)以及到达此位置的最短路径来自之前的节点计算。 2. **状态转移**:对于平地,dp[i][j] = dp[pi][pj] + cost,表示直接移动到相邻位置的时间;对于斜坡,dp[i][j] = min(dp[pi][pj], dp[pi][pj-1]) + cost/2,因为斜坡速度减半,所以需要选择更早的时刻经过。 3. **贪心策略**:有时候,为了达到全局最优,初始看起来不是最优的选择可能是正确的。但在这个问题中,贪心策略可能并不适用,因为我们不能仅依据当前状态做出决策,需要考虑到整个路径。 4. **边界条件**:初始化 dp 数组时,起点时间设为 0,其余位置设为正无穷大,保证一开始就只会向可达的位置移动。 **代码实现** 实现这样的动态规划算法通常需要用到一个优先队列(如最小堆),以便于高效地查找之前节点的最优时间。 **相关问题--:** 1. 如何设计状态转移方程来处理平地和斜坡的情况? 2. 这个问题是否存在剪枝操作以提高效率? 3. 如果网格大小非常大,如何避免存储所有 dp 值导致的空间爆炸?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值