本文已参与「新人创作礼」活动, 一起开启掘金创作之路。
【Codeforces】Codeforces Round #452 (Div. 2) E. Segments Removal | 模拟
题目链接
题目
题目大意
Vasya 有一个长度为 的整数数组 。
Vasya 在该数组上执行以下操作:每次操作,他都会找到整个数组里最长的连续相等整数段并将其删除。最长的连续相等整数段是指所有满足 的连续区间 中 最大的那一个。如果有超过一个这样的段,则选择最左边的。
例如,如果 Vasya 的数组为 ,则在一个操作后,它将变为 。
计算 Vasya 将数组变空之前可以执行的操作数量。
思路
容易发现本题无需我们进行任何决策,直接模拟即可。容易想到用双向链表存储数据,输入时遇到连续相邻的数字,可以只保留第一个,并统计数量。
找最长的连续相等整数段容易想到用优先队列进行维护,长度为第一关键字从大到小下,标为第二关键字从小到大。我们先把初始处理好的连续段的长度和下标放进优先队列里,然后只要队列不为空我们就持续操作更新操作次数,堆顶元素出堆后更新链表,如果堆顶元素的左右两边值相等,可以将其合并为一个段。
因为我们对段进行合并后,后者的信息依然在优先队列中,我们可以用一个标记数组进行判断。
代码
#include <iostream>
#include <algorithm>
#include <math.h>
#include <stdio.h>
#include <map>
#include <vector>
#include <queue>
#define nnn printf("No\n")
#define yyy printf("Yes\n")
using namespace std;
using LL=long long;
const int N=500001;
const LL mod=1000000007;
//const LL mod=998244353;
struct asdf{
int id,cnt;
bool operator < (const asdf a) const
{
if (cnt!=a.cnt) return cnt<a.cnt;
return id>a.id;
}
};
priority_queue<asdf> q;
int n,m,k,x,y,z,tot,a[N],b[N];
int pre[N],nxt[N],f[N],cnt[N];
int find(int x)
{
return x==f[x]?x:f[x]=find(f[x]);
}
LL solve()
{
scanf("%d",&n);
for (int i=1;i<=n;++i) f[i]=i,cnt[i]=1;
for (int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
pre[i]=i-1;
nxt[i]=i+1;
if (a[i]==a[i-1])
{
f[i]=find(i-1);
nxt[f[i]]=i+1;
cnt[f[i]]++;
}
}
for (int i=1;i<=n;++i)
if (f[i]==i) q.push({i,cnt[i]});
int l,r,ans=0;
while (!q.empty())
{
asdf t=q.top();
q.pop();
if (f[t.id]!=t.id) continue;
ans++;
f[t.id]=0;
if (pre[t.id]>=1&&nxt[t.id]<=n&&a[pre[t.id]]==a[nxt[t.id]])
{
nxt[find(pre[t.id])]=nxt[nxt[t.id]];
cnt[f[pre[t.id]]]+=cnt[nxt[t.id]];
q.push({f[pre[t.id]],cnt[f[pre[t.id]]]});
f[nxt[t.id]]=f[pre[t.id]];
}
else
{
nxt[find(pre[t.id])]=nxt[t.id];
pre[nxt[t.id]]=f[pre[t.id]];
}
}
cout<<ans<<endl;
}
int main()
{
int T=1;
while (T--) solve();
return 0;
}