题目链接:E. Segments Removal
题意:给你一个长为n的序列,每次对当前序列进行一次操作,就是将当前序列中最长的一段各元素相等的子段删掉,若满足题意的最长子段不止一条,则删最左边的,问你将该序列删完要操作多少次?
看到这种删除,合并的操作,我们可以想到并查集或者set来做是很方便的。
然后我们就要去想怎么去用set维护。
我们开两个set,一个存,块的长度和编号,另一个存块的编号,这样我们就可以找到每个块前面的位置,和后面的位置。
然后每次取出一块,再看前后两块是否可以合并即可。
AC代码:
#pragma GCC optimize(2)
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int N=2e5+10;
int n,a[N],sum[N],res;
struct node{int len,id;};
bool operator < (node a,node b){return a.len==b.len?a.id<b.id:a.len>b.len;}
set<node> st;
set<int> s;
signed main(){
cin>>n; s.insert(0); s.insert(n+1); sum[n+1]=-1;
st.insert({0,0}); st.insert({0,0});
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++){
if(a[i]==a[i-1]) sum[i]=sum[i-1]+1;
else sum[i]=1;
if(a[i]!=a[i+1]) st.insert({sum[i],i}),s.insert(i);
}
while(1){
node u=*st.begin(); if(u.len<=0) break;
st.erase(st.begin()); res++;
auto x=s.find(u.id); auto y=x; y++; x--; s.erase(u.id);
if(a[*x]==a[*y]){
st.erase({sum[*x],*x}); st.erase({sum[*y],*y});
sum[*y]+=sum[*x]; s.erase(*x);
st.insert({sum[*y],*y});
}
}
cout<<res<<endl;
return 0;
}