题意 : 给出一个序列,然后每次将重复出现的元素进行求和合并(若有多个,则优先取最小的进行合并),若某重复元素有很多,那么取最左边的那两个进行合并且合并后元素位于原来右边元素的位置,例如 3 2 6 2 2 这里 2 是重复元素,取最左边的两个 2 进行一次求和合并,合并后将变成 4 ,且这个 4 的位置位于较右边的 2 的位置,即序列变成 3 6 4 2。进行这样的操作,直到没有元素重复为止,输出最终序列。
分析 :
考察模拟能力
首先注意到,输出最后的序列
只需要知道各个元素的相对位置即可
不必知道元素真正的位置下标是多少
即不用真正实时更新位置下标,因为这个操作会很麻烦
对于整个序列,需要知道哪些数是重复了的
这里用一个 set 维护
而对于每一个重复的数,又要知道其每个元素的位置
而且最好让这些位置升序排序,才方便取出最左边的两个进行合并
考虑此题的数据量并不是很大
所以考虑使用 map< int, set<int> > 来维护
最后就是模拟操作即可
所有的操作完成后答案就存在于 map 中
将元素信息取出来,然后丢到一个 vector 中
排序输出即可
#pragma GCC optimize("O3") #define LL long long #define pi pair<long long, long long> #define MK(i, j) make_pair(i, j) #include <bits/stdc++.h> using namespace std; map<LL, set<LL> > mp; set<LL> s; vector<pi> ans; int N; LL num; int main(void) { ios_base::sync_with_stdio(0); cin.tie(0); cin>>N; for(int i=0; i<N; i++){ cin>>num; mp[num].insert(i); if(mp[num].size() > 1) s.insert(num); } while(!s.empty()){ set<LL>::iterator it = s.begin(); mp[(*it)].erase(mp[(*it)].begin()); mp[(*it)<<1].insert((*mp[(*it)].begin())); mp[(*it)].erase(mp[(*it)].begin()); if(mp[(*it)<<1].size() > 1) s.insert((*it)<<1); if(mp[(*it)].size() < 2) s.erase((*it)); } for(map<LL, set<LL> >::iterator it=mp.begin(); it!=mp.end(); it++){ if((*it).second.size() > 0){ ans.push_back(MK(*(*it).second.begin(), (*it).first)); } } sort(ans.begin(), ans.end()); cout<<ans.size()<<endl; for(vector<pi>::iterator it=ans.begin(); it!=ans.end(); it++) cout<<(*it).second<<" "; cout<<endl; return 0; }