题意:给定长度为N的序列,每次将序列中长度最大部分相等的区间删去(若有多种情况删去最左端的区间),问多少次操作可以将删完。

思路

怎样知道前后区间表示的数字能否合并?

模拟链表,用两个数组pre和nxt。

删掉之后前后区间合并之后,放在本来的优先队列里的原前后区间该怎么处理?

再开一个优先队列用来放置被删除的区间,优先级与原优先队列相同,如果堆顶等于堆顶就删掉。

pair在优先队列中的优先级:

如果是大顶堆,first从大到小,first相等,second从大到小,所以这里second(第几个)要取相反数。

这里注意合并区间之后,pos后面的后面一个的前驱应该是pre[pos],不要改了nxt就觉得pre不用改了,这里不是单向链表从前往后遍历,而是双向的。

代码

int pre[200010],nxt[200010];//每一块的前驱和后续(模拟链表)
int num[200010],len[200010];//每一块的数字和长度
int main()
{
    int n;
    scanf("%d",&n);
    int cnt=0;int x,ls=-1;
    for(int i=0;i<n;i++)
    {
        scanf("%d",&x);
        if(x!=ls)num[++cnt]=x,len[cnt]=1;
        else len[cnt]++;
        ls=x;
    }
    pre[1]=-1;nxt[cnt]=-1;
    priority_queue<pair<int,int> >q,del;
    for(int i=1;i<=cnt;i++)
    {
        if(i!=1)pre[i]=i-1;
        if(i!=cnt)nxt[i]=i+1;
        q.push(make_pair(len[i],-i));//因为是大顶堆
    }
    int ans=0;
    while(cnt)
    {
        while(!del.empty()&&q.top()==del.top())q.pop(),del.pop();//把原队列中该删除的删除
        pair<int,int>temp;
        temp=q.top();
        //printf("%d %d\n",temp.first,-temp.second);
        ans++;
        q.pop();
        int pos=-temp.second;
        if(pre[pos]!=-1&&nxt[pos]!=-1&&num[pre[pos]]==num[nxt[pos]])//向前合并
        {
            //printf("!\n");
            del.push(make_pair(len[pre[pos]],-pre[pos]));
            del.push(make_pair(len[nxt[pos]],-nxt[pos]));
            q.push(make_pair(len[pre[pos]]+len[nxt[pos]],-pre[pos]));
            len[pre[pos]]+=len[nxt[pos]];
            nxt[pre[pos]]=nxt[nxt[pos]];pre[nxt[nxt[pos]]]=pre[pos];//注意pos后面的后面一个的前驱应该是pre[pos]
            cnt-=2;
        }
        else//在链表中删除该块
        {
            nxt[pre[pos]]=nxt[pos];pre[nxt[pos]]=pre[pos];
            cnt--;
        }
    }
    printf("%d\n",ans);
    return 0;
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

CodeForces899F 线段树中前缀和的查找 Previous
CodeForces899D 构造 Next