http://codeforces.com/contest/962/problem/D
2 seconds
256 megabytes
standard input
standard output
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] 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].
If the given array is look like [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].
The first line contains a single integer nn (2≤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≤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.
In the third example all integers in the given array are distinct, so it will not change.
思路
第一感觉是暴力,可是要怎样暴力呢?这个数据范围达到了十的九次方,数据量更是有15万,即使要暴力,也要有足够的技巧,现在在我面前的问题有,如何判断一个数组里面,已经没有相同的数了,还有要怎样在短时间内找出相同的两个数,还要最小的,并且还不能改变原数列的顺序。如果要擦除的话或许可以用vector,不过从以上的问题来看,这题应该不是一个简单的模拟。是结论题也有可能。我还想到了动态规划,因为最近做动态规划比较多,不知道这个直觉是否正确。不过,我还是打算暴力试一下,毕竟ABC全是傻逼题,当然C题难住了我这个傻逼,没有想到它是傻逼题:
我并没有使用vector,擦除的话,我直接将其变为0,输出的时候跳过就好了,整个算法的时间复杂度是n*1e9;
#include<stdio.h>
int a[150024];
const int m=1000000000;
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
}
int sum=n;
int num=0;
int j=0;
for(int i=1;i<=m;i++){
j=0;num=0;
for(int k=0;k<n;k++){
if(a[k]==i){
num++;
if(num==1){j=k;}
else if(num==2){sum--;a[j]=0;a[k]=2*i;num=0;}
}
}
}
printf("%d\n",sum);
for(int i=0;i<n;i++){
if(a[i]!=0){printf("%d ",a[i]);}
}
}
当我让m是1e9时,本地的小数据都跑不完,我知道肯定是会超时的,不过我还是要交交看。
结果竟然不是超时,真是气死我了;原来是把m写成了8,改成了500试试,不改成1e9是因为真的会超时啊
转念一想,反正过不了,何不去交交看呢?
事实证明我的结论是正确的,那么这题的正解是什么呢?好难啊!!!看来我真的是个傻逼。
话是这么说,可是这题还是应该是模拟,只不过我的方法不对,毕竟还要求输出改变后的数列。不如从左向右遍历,遇到和自己一样的就把自己阉了,这样的话时间复杂度会是n^2,n是150000,那么假设是1e5,那么n方就是1e10,而我刚刚把m调到1e7是,超了时,此时的n是500,那么就是1e9,那么还是超了。。。
算了,我还是放弃无畏的抵抗,去看题解吧。
emmmm,找到一个和我的方法相似的
果然我还是太蠢了 https://blog.csdn.net/xiaofeng187/article/details/79912959
其实也是暴力,只不过一开始我考虑到要维护队列的顺序,所以没有进行排序,其实是可以排序的,记录下编号,最后再按编号排序就行了,可是马上就有问题,1,1,1,1,1,2这种情况,我们该如何合并,还是看代码吧:
#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;
struct node
{
unsigned long long x;
int id;
bool operator < (const node & a) const {
if (x == a.x) {
return a.id < id;
}
return a.x < x;
}
}s;
struct tre
{
unsigned long long x;
int id;
}ans[150000];
bool jud(tre o,tre p)
{
return o.id<p.id;
}
int main()
{
priority_queue<node> a;
int n;
cin>>n;
int q,w;
for(int i=0;i<n;i++){
cin>>s.x;
s.id=i;
a.push(s);
}
node k,j;
int t=0;
while(a.size()!=0){
//cout<<" " " "<<a.size()<<endl;
if(a.size()==1){
k=a.top();
a.pop();
ans[t].x=k.x;
ans[t].id=k.id;
t++;
//cout<<a.size()<<endl;
}
else{
k=a.top();a.pop();
j=a.top();a.pop();
if(k.x==j.x){
j.x*=2;
a.push(j);
}
else{
//if(k.x==0){continue;}
ans[t].id=k.id;
ans[t].x=k.x;
//cout<<ans[t].x;
t++;
a.push(j);
}
}
}
sort(ans,ans+t,jud);
cout<<t<<endl;
for(int i=0;i<t;i++){
cout<<ans[i].x<<" ";
}
cout<<endl;
}