P4559 [JSOI2018]列队 解题报告

这篇博客详细介绍了如何利用贪心策略和主席树解决JSOI2018中的一道竞赛题目。题目要求在假定的情景下,计算最小的点位移动次数,保持所有点的相对顺序不变。博主通过建立主席树并进行分类讨论,成功实现了高效查询,给出了解题报告和完整代码实现。

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

P4559 [JSOI2018]列队 解题报告

一道好题。

link

题目大意

数轴上有 n n n 个点,每个点依次编号为 1 ⋯ n 1\cdots n 1n,且具有一个坐标 x i x_i xi q q q 次询问,每次 假定使编号为 [ l , r ] [l,r] [l,r] 的所有点移动到坐标为 [ K , K + r − l ] [K,K+r-l] [K,K+rl] 的连续一段,求最小总移动次数。注意是假定,不会真的移动。

1 ≤ n , m ≤ 5 × 1 0 5 , 1 ≤ x i , K ≤ 1 0 6 1\le n,m\le 5\times 10^5, 1\le x_i,K\le 10^6 1n,m5×105,1xi,K106

解题报告

贪心策略可证明,一种最优的方案是每个点相对位置不改变,即按坐标排序,第一名在 K K K,第二名在 K + 1 K+1 K+1 ……依此类推。

这个编号为 [ l , r ] [l,r] [l,r] ,很容易想到主席树。接下来的查询只要在主席树上分类讨论就可以了。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
char In[1 << 20], *ss = In, *tt = In;
#define getchar() (ss == tt && (tt = (ss = In) + fread(In, 1, 1 << 20, stdin), ss == tt) ? EOF : *ss++)
ll read() {
	ll x = 0, f = 1; char ch = getchar();
	for(; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') f = -1;
	for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + int(ch - '0');
	return x * f;
}
const int MAXN = 5e5 + 5;
const int MAXK = 1e6 + 5, K = 1e6;
namespace Sgt {
	const int MAXNODE = MAXK << 5;
	struct {int ls, rs; ll sz, sum;}e[MAXNODE];
	int tot;
	void mdy(int& p, int pre, int l, int r, ll x) {
		p = ++tot; e[p] = e[pre];
		e[p].sz++; e[p].sum += x;
		if(l == r) return;
		int m = (l + r) >> 1;
		if(x <= m) mdy(e[p].ls, e[pre].ls, l, m, x);
		else mdy(e[p].rs, e[pre].rs, m+1, r, x);
	}
	ll qry(int u, int v, int l, int r, ll k) {
		ll tsz = e[v].sz - e[u].sz, tsum = e[v].sum - e[u].sum;
		if(tsz == 0) return 0;
		if(r <= k + tsz - 1) return tsz * (2 * k + tsz - 1) / 2 - tsum;
		else if(l >= k) return tsum - tsz * (2 * k + tsz - 1) / 2;
		else {
			int m = (l + r) >> 1; ll lsz = e[e[v].ls].sz - e[e[u].ls].sz;
			return qry(e[u].ls, e[v].ls, l, m, k) + qry(e[u].rs, e[v].rs, m+1, r, k+lsz);
		}
	}
}
using namespace Sgt;
int n, m, rt[MAXN];
int main() {
	n = read(), m = read();
	for(int i = 1; i <= n; i++) {
		mdy(rt[i], rt[i-1], 1, K, read());
	}
	for(int i = 1; i <= m; i++) {
		int l = read(), r = read(), k = read();
		printf("%lld\n", qry(rt[l-1], rt[r], 1, K, k));
	}
	return 0;

}
### JSOI2008 星球大战 题目描述 在一个遥远的星系,一个黑暗的帝国依靠其超级武器统治着整个星系。为了反抗帝国的暴政,联盟计划通过一系列行动来削弱帝国的力量。在这个过程中,联盟会逐步摧毁一些星球上的基地,从而影响到这些星球之间的通信连接。 初始状态下,所有的星球都是相互连通的。每次攻击将会摧毁一颗星球及其上所有与其他星球的连线。给定星球的数量以及被摧毁星球的顺序,计算每一次攻击之后剩余星球间的联通分量数量[^2]。 ### 解法概述 此问题可以通过逆向思维加并查集的数据结构解决。具体来说: - 将输入数据倒序处理,即假设最后被摧毁的星球最先恢复。 - 使用并查集维护当前存在的星球集合及其连通关系。 - 对于每一个新加入(实际上是旧删除)的星球,检查它原本相邻的所有其他星球是否已经存在于当前图中;如果存在,则将它们所在的两个不同集合合并起来。 - 记录下每次操作后的连通域数目变化情况,并最终按照时间轴反向输出结果。 这种方法能够有效地追踪随着星球逐渐消失而导致的变化过程中的连通区域数量改变状况[^3]。 ```python def find(parent, i): if parent[i] != i: parent[i] = find(parent, parent[i]) return parent[i] def union(parent, rank, x, y): rootX = find(parent, x) rootY = find(parent, y) if rootX != rootY: if rank[rootX] > rank[rootY]: parent[rootY] = rootX elif rank[rootX] < rank[rootY]: parent[rootX] = rootY else: parent[rootY] = rootX rank[rootX] += 1 n, m = map(int, input().split()) edges = [] for _ in range(m): u, v = map(int, input().split()) edges.append((u - 1, v - 1)) destroy_order = list(map(lambda x: int(x) - 1, input().split())) parent = [i for i in range(n)] rank = [0] * n connected_components = n result = [] # Reverse process of destruction as restoration. restore_order = destroy_order[::-1] restored_planets = set() for planet_to_restore in restore_order: result.append(connected_components) restored_planets.add(planet_to_restore) for edge in edges: if planet_to_restore in edge and all(p in restored_planets for p in edge): connected_components -= (find(parent, edge[0]) != find(parent, edge[1])) union(parent, rank, edge[0], edge[1]) print("\n".join(str(cc) for cc in reversed(result))) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

日居月诸Rijuyuezhu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值