[SCOI2011]棘手的操作

[SCOI2011]棘手的操作

https://ac.nowcoder.com/acm/problem/20282

[SCOI2011]棘手的操作

前言

  • 不得不说,这题得劲。因为这几天复习数据结构都疯了QwQ,一来就想码并查集+线段树,但是想了一想(wtcl),似乎不太容易维护每个块。emmm,然后去网上看看题解,发现竟然可以用堆合并与删除解决,以简单方法吊打难题
    图片说明

分析

  • 纵观全局,可以先确定能用并查集。每一个都需要维护。而需要维护的则是每一个块的最大值,每一个块有哪些节点
  • 维护方法:因为既要求所有节点中的最大值,也要求各个块中的,那就对全局以及每一个块都建立一个堆,同时维护一个容器 c [ i ] 表示在块 i 中的有哪些节点。因为每次对一个点进行修改时,不能直接提出来进行修改(即先进行pop找到节点,修改完后在push回去),那么换种方式,对每一个块再建立一个队列del存下本应该删去的值,每一次取出块中的最大值的时候,先与队列del中的数进行比较
  • 详细过程:
    U x v:合并x,v所在的两个堆。为了减少时间复杂度,选择把块中节点较少的合并到块中节点较多的。不断取出其中一个堆中的元素,加入到另一个中去
    A1 x v:先把它push进入堆del中,修改完再入队
    A2 x v:只需要对每一个联通块维护一个变量 jub [ i ] ,与线段树中的懒标记类似
    A3 x v:同上,只需要用一个全局变量记录一下
    F1 x:输出 a [ i ] + jub [ i对应块的编号 ],就像一个下传操作
    F2 x:每一个连通块都建立了一个堆,输出堆顶
    F3 x:在全局也建立了一个堆,输出堆顶

代码

/*
U  x y:合并他们的两个堆

A1 x v:先pop到del,再push回val

A2 x v:连通块的变量处理

A3 x v:全局变量处理

F1 x:找到所属连通块,加上局部和全局变量

F2 x:直接pop

F3 x:整体的pop
*/
#include<bits/stdc++.h>

#define ll long long

using namespace std;

const int N=3e5+10;

int n,zt,m;
ll a[N],jub[N];int f[N];

vector<int>num[N];
//连通块里的点
struct nkl
{
    priority_queue<ll>q,del;

    void push(ll x){q.push(x);}
    void pop(ll x){del.push(x);}
    int top()
    {
        while(del.size()&&del.top()==q.top())
            del.pop(),q.pop();
        return q.top();
    }
}wl,bl[N];

inline int find(int x)
{
    if(x==f[x]) return x;
    return f[x]=find(f[x]);
}

int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        f[i]=i;//并查集初始化
        num[i].push_back(i);//连通块的点
        wl.push(a[i]);
        bl[i].push(a[i]);
        //全局与局部的点 
    }

    scanf("%d",&m);char ins[5];int x,v;
    while(m--)
    {
        scanf("%s",ins);
        if(ins[0]=='U')
        {
            scanf("%d%d",&x,&v);
            x=find(x),v=find(v);
            if(x==v) continue;

            if(num[x].size()<num[v].size()) swap(x,v);
            wl.pop(bl[v].top()+jub[v]);
            wl.pop(bl[x].top()+jub[x]);
            int len=num[v].size();
            for (int i=0;i<len;i++)
            {
                int j=num[v][i];
                f[j]=x;
                a[j]=a[j]+jub[v]-jub[x];
                bl[x].push(a[j]);
                num[x].push_back(j);
            }//开始合并
            num[v].clear();
            wl.push(bl[x].top()+jub[x]);
            //重回最大值
        }
        else if(ins[0]=='A')
        {
            if(ins[1]=='1')
            {
                scanf("%d%d",&x,&v);
                int u=find(x);
                wl.pop(bl[u].top()+jub[u]);
                bl[u].pop(a[x]);
                a[x]+=v;
                bl[u].push(a[x]);
                wl.push(bl[u].top()+jub[u]);
            }
            else if(ins[1]=='2')
            {
                scanf("%d%d",&x,&v);int u=find(x);
                wl.pop(bl[u].top()+jub[u]);
                jub[u]+=v;
                wl.push(bl[u].top()+jub[u]);
            }
            else scanf("%d",&v),zt+=v;
        }
        else
        {
            if(ins[1]=='3')
                printf("%lld\n",wl.top()+(ll)zt);
            else
            {
                scanf("%d",&x);
                int u=find(x);
                if(ins[1]=='1')
                    printf("%lld\n",(ll)a[x]+zt+jub[u]);
                else
                    printf("%lld\n",(ll)bl[u].top()+zt+jub[u]);
            }
        }
    }

    return 0;
}
每日一题 文章被收录于专栏

每天的题,为了牛币

全部评论

相关推荐

11-03 14:38
重庆大学 Java
AAA求offer教程:我手都抬起来了又揣裤兜了
点赞 评论 收藏
分享
10-09 22:05
666 C++
找到工作就狠狠玩CSGO:报联合国演讲,报电子烟设计与制造
点赞 评论 收藏
分享
3 收藏 评论
分享
牛客网
牛客企业服务