Codeforces 962D(优先队列)
You are given an array of positive integers. While there are at least two equal elements, we will perform the following operation. We choose the smallest value xx that occurs in the array 22 or more times. Take the first two occurrences of xx in this array (the two leftmost occurrences). Remove the left of these two occurrences, and the right one is replaced by the sum of this two values (that is, 2⋅x2⋅x).
Determine how the array will look after described operations are performed.
For example, consider the given array looks like [3,4,1,2,2,1,1][3,4,1,2,2,1,1]. It will be changed in the following way: [3,4,1,2,2,1,1] → [3,4,2,2,2,1] → [3,4,4,2,1] → [3,8,2,1][3,4,1,2,2,1,1] → [3,4,2,2,2,1] → [3,4,4,2,1] → [3,8,2,1].
If the given array is look like [1,1,3,1,1][1,1,3,1,1] it will be changed in the following way: [1,1,3,1,1] → [2,3,1,1] → [2,3,2] → [3,4][1,1,3,1,1] → [2,3,1,1] → [2,3,2] → [3,4].
The first line contains a single integer nn (2≤n≤1500002≤n≤150000) — the number of elements in the array.
The second line contains a sequence from nn elements a1,a2,…,ana1,a2,…,an (1≤ai≤1091≤ai≤109) — the elements of the array.
In the first line print an integer kk — the number of elements in the array after all the performed operations. In the second line print kk integers — the elements of the array after all the performed operations.
7 3 4 1 2 2 1 1
4 3 8 2 1
5 1 1 3 1 1
2 3 4
5 10 40 20 50 30
5 10 40 20 50 30
The first two examples were considered in the statement.
最后需要输出操作后的数组的大小和数组。
这道题的题意比较绕,所以让我们根据样例来更直观的阐述一下题意。
对于第一个样例7\n 3 4 1 2 2 1 1
在数组中1和2均在数组中出现过两次或以上,那么我们优先取这两个数中的最小的数1进行操作。
于是我们按照1所在数组中的位置先选出处于位置3和位置6的数,将位置3的数删除,将位置为6的数乘2,此时的数组变为3 4 2 2 2 1
因为数组中2在数组中出现了两次,因此继续对2进行操作,按顺序取出处于如今位置3的2和处于如今为位置4的2,删除前一个,后一个乘2,因此此时的数组变为3 4 4 2 1。
因为4仍然满足调节,故继续对4进行操作,则数组变为3 8 2 1,而在此时,数组中不再存在一个出现过两次或以上的数,因此停止操作,输出数组大小4和数组元素3 8 2 1
解法:因为我们需要每一次都需要对数组中最小的数进行分析,首先想到的应该是sort排序,但在这题看来可能会比较繁琐。因此我们想到可以通过**优先队列**对数列中最值进行高效的维护。
因此我们可以开一个由数值和位置组成的结构体,让优先队列同时维护数值和位置。(注意当数值相同时,需要返回位置靠前的数,否则将会使位置靠后的数值删除)
不断的pop出队首的两个数(此处有一个小小的注意点,详见代码)假如两个数相同,则舍弃前一个数,并将后一个数的数值*2并重新压入优先队列;假如两个数不同,意味着前一个数的值已经最优,故存入用来记录答案的ans数组,然后继续将后一个数重新压入队列。不断进行操作,直到无法操作。
最后因为ans数组中所保存的数是乱的,因此只需要再按照位置进行排序即可。
(注意可能会爆int,得用long long)!!!
#include <bits/stdc++.h>
#define maxn 150005
using namespace std;
typedef long long ll;
struct number{//第一个结构体用来输入的数
ll a;//用来记录数值
int id;//用来记录位置
bool operator<(const number &b) const{//优先队列重载
if(b.a==a){//如果数值相同,则返位置靠前的数
return b.id<id;
}
return b.a<a;//否则返回数值小的
}
}q[maxn];
struct num{//第二个结构体用来记录答案
ll ans;//答案的值
int id;//答案所在的位置
}p[maxn];
bool cmp(num a,num b){//用来使最后的答案按位置升序排序
return a.id<b.id;
}
int main()
{
int n;
cin>>n;
priority_queue<number> que;//建立一个储存number的优先队列
for(int i=1;i<=n;i++){
cin>>q[i].a;//输入数值
q[i].id=i;//记录位置
que.push(q[i]);//压入优先队列
}
int cnt=0;//答案的大小
while(que.size()>1){//因为要保证一直可以取到两个数,因此需要满足队列大小大于1,最后再把最后的数pop出即可
number tmpa=que.top();//取队首第一个数
que.pop();
number tmpb=que.top();//取队首第二个数
que.pop();
if(tmpa.a!=tmpb.a){//如果两个数不相同,则统计a的答案,重新压入b
p[cnt].ans=tmpa.a;
p[cnt++].id=tmpa.id;
que.push(tmpb);
continue;
}
else if(tmpa.a==tmpb.a){//如果两个数相同,则不对a操作,更新b的值,并将b重新压入优先队列
tmpb.a=tmpb.a*2;
que.push(tmpb);
}
}
while(!que.empty()){//把最后队列中一个数再记录下来
number xx=que.top();
que.pop();
p[cnt].ans=xx.a;
p[cnt++].id=xx.id;
}
sort(p,p+cnt,cmp);//将答案重新根据位置升序排序
cout<<cnt<<endl;//输出操作后数组的大小
for(int i=0;i<cnt;i++){//输出
if(i==0) cout<<p[i].ans;
else cout<<" "<<p[i].ans;
}
return 0;
}