CF962D Merge Equals 题解

本文解析了如何使用优先队列(小根堆)和有序下标对来解决MergeEquals问题,通过合并出现次数大于等于2的最小值及其对应下标,直至序列无法操作。关键步骤包括维护元素值和下标对,以及有序答案序列的输出。

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

Merge Equals 题解

题目大意

给定正整数序列 a[1…N]a[1\dots N]a[1N] ,每次你需要找到序列中出现次数 >=2>=2>=2 的最小值 xxx ,找到 xxx222 个最小下标 (i,j)(i,j)(i,j) ,删除 aia_iai,将 aja_jaj 改为 2x2x2x 。反复进行如此操作,直到不能操作位置,求序列最终的形态。

解析

根据题意,我们知道我们要找的数,要满足以下条件:

(1) 出现次数>=2
(2) 尽可能的小

对于条件2,我们很容易的想到用小根堆维护,也就是优先队列。但是题目中还需要用到这个数的下标,所以很容易想到维护点对 pair(x,pos)pair(x,pos)pair(x,pos) ,其中 xxx 表示值, pospospos 表示 xxx 在原数组中的位置。

我们把样例中数据排个序,得到的结果是:

[1,3],[1,6],[1,7],[2,4],[2,5],[3,1],[4,2][1,3],[1,6],[1,7],[2,4],[2,5],[3,1],[4,2][1,3],[1,6],[1,7],[2,4],[2,5],[3,1],[4,2]。很明显,我们要合并开头的两个数。

从刚才的例子中可以发现,我们每次取数,只需要处理开头的两个元素即可。我们就对开头的两个元素进行分类讨论:(设开头的两个元素分别为 [x1,p1][x_1,p_1][x1,p1][x2,p2][x_2,p_2][x2,p2] ,且 x1⩽x2x_1\leqslant x_2x1x2

(1) x1=x2x_1=x_2x1=x2

这种情况很简单,我们把 [2x1,p2][2x_1,p_2][2x1,p2] 放入队列中即可。

(2)x1≠x2x_1\ne x_2x1=x2

由于 x1⩽x2x_1\leqslant x_2x1x2 ,且 x1≠x2x_1\ne x_2x1=x2 ,可得 x1<x2x_1<x_2x1<x2 。又因为 x1x_1x1x2x_2x2 是优先队列中最小的数,所以 x1x_1x1 不会找到跟他相等的数,所以直接把 x1x_1x1 放入答案序列中即可, x2x_2x2 则要重新放回队列。

最后,在答案序列中输出即可。这里可能会碰到一个问题:因为之前的优先队列是根据元素值进行排序的,也就是说,数组下标可能是无序的。但是输出答案序列的时候,我们要求数组下标是有序的。所以,我们还可以新开一个优先队列,并让下表位置作为第一关键字即可。

算法流程

元素值相等
元素值不等
队列不为空
队列为空
程序开始
将序列转化成元素,存入优先队列
取出队首两个元素
合并,插入优先队列
第一个元素:答案序列,第二个元素:重新插入队列
重复操作
输出答案序列
程序结束

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
	int s = 0, w = 1;
	char ch = getchar();
	for(; ch < '0' || ch > '9'; w *= (ch == '-') ? -1 : 1, ch = getchar());
	for(; ch >= '0' && ch <= '9'; s = 10 * s + ch - '0', ch = getchar());
	return s * w;
}
int n;
priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > pq;
priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > ans;
signed main(){
	n = read();
	for(int i = 0; i < n; i++){
		int tmp = read();
		pq.push(make_pair(tmp, i));
	}
	while(pq.size() > 1){
		pair<int, int> a, b;
		a = pq.top(), pq.pop();
		b = pq.top(), pq.pop();
//		printf("(%d,%d) (%d,%d)\n", a.first, a.second, b.first, b.second);
		if(a.first == b.first){
//			printf("push(%d, %d)\n", a.first * 2, b.second);
			pq.push(make_pair(a.first * 2, b.second));
		} else {
			pq.push(b);
			ans.push(make_pair(a.second, a.first));
		}
	}
	ans.push(make_pair(pq.top().second, pq.top().first));
	cout << ans.size() << endl;
	while(!ans.empty()){
		pair<int, int> now = ans.top();
		ans.pop();
		cout << now.second << " ";
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值