题目链接: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;
}