Codeforces Round #452 (Div. 2) E. Segments Removal(并查集 + 优先队列)

40 篇文章 0 订阅
12 篇文章 0 订阅

题目链接:http://codeforces.com/contest/899/problem/E

题目大意:给你一个长度为 n 的数组,每次操作只能消去数组中最长连续的数,问消去整个数组需要操作几次。

题目思路:一开始是想用线段树维护,但是由于太鶸了维护不动。。。后来想着用并查集维护每个连续数集合的左端点和右端点,再用优先队列来找最长的集合,经历了无数次的WA,TLE,MLE(虽然至今不知道为啥256M的内存能被我艹爆了。。。)之后终于A了。(看大神的代码还有直接用数组维护的,看了好几遍还是没看懂是如何操作的,果然弱爆了。。。orz)。

并查集的具体实现就是用一个l[i]来表示第 i 个数的集合的左端点,r[i]来表示第 i 个数的集合的右端点,

在消去的时候判断左端点左边和右端点右边的数是否相同,如果相同就更新左边集合的右端点和右边集合的左端点,

以及集合中数的个数,最后再借用优先队列进行维护操作即可。

具体代码如下:

#include <bits/stdc++.h>
using namespace std;
const int MX = 2e5+7;

int n,a[MX];
int num[MX],l[MX],r[MX];

int Find(int P[],int x){
    return P[x] == x ? x : (P[x] = Find(P,P[x]));
}

void Union(int u,int v){
    int uu = Find(l,u),vv = Find(l,v);
    num[uu] += num[vv];
    l[vv] = l[uu];
}

struct node{
    int pos,len;

    bool operator <(const node &node1)const{
        if(len == node1.len) return pos > node1.pos;
        return len < node1.len;
    }
};

int main(){
    scanf("%d",&n);
    priority_queue<node>q;
    for(int i = 1;i <= n;i++){
        scanf("%d",&a[i]);
        l[i] = r[i] = i;
        num[i] = 1;
    }
    a[n+1] = -1;
    for(int i = 1;i <= n;i++){
        if(a[i] == a[i-1]) Union(i-1,i);
        if(a[i] == a[i+1]) r[i] = i+1;
    }
    l[n+1] = r[n+1] = n+1;
    for(int i = 1;i <= n;i++){
        if(l[i] == i){
            q.push(node{i,num[i]});
        }
    }
    int ans = 0;
    while(!q.empty()){
        node nw = q.top();q.pop();
        int x = nw.pos;
        if(l[x] != x) continue;
        ans++;
        int R = Find(r,x);
        l[x] = x - 1;
        r[R] = R + 1;
        int x1 = Find(l,x),x2 = Find(l,Find(r,x));
        if(a[x1] != a[x2]) continue;
        Union(x1,x2);
        r[Find(r,x1)] = x2;
        q.push(node{x1,num[x1]});
    }
    printf("%d\n",ans);
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值