【bzoj4631】踩气球(可并堆+并查集)

版权声明:转载请声明 https://blog.csdn.net/ezoiHQM/article/details/82555284

权限题题目链接
这道题其实是一道可并堆的经典题。
每一个盒子维护一个小根堆,存储以这个点为左端点的右端点。一旦一个盒子里的被取空,就将这个盒子和下一个盒子合并,然后不断判断这个堆的堆顶是否小于下一个盒子的编号来统计答案。合并就用左偏树+并查集啦。
如果有误在评论区吼一声哦!
代码:

#include<cstdio>
#include<cctype>
#include<algorithm>
using namespace std;
int n,m,q,lastans,a[100010],rt[100010],lch[100010],rch[100010],v[100010],dep[100010],f[100010];
int find(int x){
    return x==f[x]?x:f[x]=find(f[x]);
}
int merge(int x,int y){
    if(!x||!y)
        return x|y;
    if(v[x]>v[y])
        swap(x,y);
    rch[x]=merge(rch[x],y);
    if(dep[lch[x]]<dep[rch[x]])
        swap(lch[x],rch[x]);
    dep[x]=dep[rch[x]]+1;
    return x;
}
int rd(){
    int x=0;
    char c;
    do c=getchar();
    while(!isdigit(c));
    do{
        x=(x<<1)+(x<<3)+(c^48);
        c=getchar();
    }while(isdigit(c));
    return x;
}
int main(){
    n=rd(),m=rd();
    for(int i=1;i<=n;i++){
        a[i]=rd();
        f[i]=i;
    }
    f[n+1]=n+1;
    for(int i=1;i<=m;i++){
        int l=rd(),r=rd();
        v[i]=r;
        rt[l]=merge(rt[l],i);
    }
    q=rd();
    while(q--){
        int x=rd();
        x=(x+lastans-1)%n+1;
        a[x]--;
        if(!a[x]){
            f[x]=find(x+1);
            while(rt[x]&&v[rt[x]]<f[x]){
                lastans++;
                rt[x]=merge(lch[rt[x]],rch[rt[x]]);
            }
            rt[f[x]]=merge(rt[f[x]],rt[x]);
        }
        printf("%d\n",lastans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ezoiHQM/article/details/82555284