BZOJ 2333 SCOI 2011 棘手的操作 可并堆

做此题的原因

  • 题号美

题目大意

给出一个序列,支持一堆操作(具体看下面)。让你维护它。

思路

U x y:我们需要可并堆来将两个堆合并。

A1 x v:将这个点从堆中拽出来,改了之后再合并回去。

A2 x v:在堆顶打标记。

A3:记录一个全局变量记录。

F1 x:将这个点到堆顶的链上的所有标记下传,之后返回自己的大小。

F2 x:返回堆顶。

F3:用一个堆(set也行)维护所有堆顶的元素。需要仔细讨论一下。

CODE

#define _CRT_SECURE_NO_WARNINGS

#include <set>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 300010
using namespace std;

multiset<int> G;

struct Heap{
    int val,plus;
    Heap *son[2],*father;

    Heap(int _):val(_) {
        son[0] = son[1] = father = NULL;
        plus = 0;
    }
    Heap() {}
    void Plus(int _) {
        val += _;
        plus += _;
    }
    void PushDown() {
        if(plus) {
            if(son[0] != NULL)  son[0]->Plus(plus);
            if(son[1] != NULL)  son[1]->Plus(plus);
            plus = 0;
        }
    }
}heap[MAX];

Heap *Find(Heap *x)
{
    while(x->father != NULL)
        x = x->father;
    return x;
}

inline Heap *Merge(Heap *x,Heap *y)
{
    if(x == NULL)   return y;
    if(y == NULL)   return x;
    if(x->val < y->val) swap(x,y);
    x->PushDown();
    bool k = rand()&1;
    x->son[k] = Merge(x->son[k],y);
    x->son[k]->father = x;
    return x;
}

inline void Union(Heap *x,Heap *y)
{
    G.erase(G.find(min(x->val,y->val)));
    Merge(x,y);
}

inline void ClearTag(Heap *x)
{
    static Heap *stack[MAX];
    int top = 0;
    while(x != NULL)
        stack[++top] = x,x = x->father;
    for(int i = top; i; --i)
        stack[i]->PushDown();
}

inline void PlusPoint(Heap *x,int c)
{
    if(x->father == NULL) {
        G.erase(G.find(x->val));
        x->PushDown();
        Heap *temp = Merge(x->son[0],x->son[1]);
        if(temp != NULL)
            temp->father = NULL;
        x->val += c;
        x->son[0] = x->son[1] = NULL;
        G.insert(Merge(x,temp)->val);
        return ;
    }
    Heap *fx = Find(x);
    G.erase(G.find(fx->val));
    ClearTag(x);
    Heap *temp = Merge(x->son[0],x->son[1]);
    bool k = x->father->son[1] == x;
    x->father->son[k] = temp;
    if(temp != NULL)
        temp->father = x->father;
    x->father = NULL;
    x->val += c;
    x->son[0] = x->son[1] = NULL;
    G.insert(Merge(fx,x)->val);
}

inline void PlusBlock(Heap *x,int c)
{
    Heap *fx = Find(x);
    G.erase(G.find(fx->val));
    fx->Plus(c);
    G.insert(fx->val);
}

int g_add;

inline void PlusAll(int c)
{
    g_add += c;
}

inline int AskPoint(Heap *x)
{
    ClearTag(x);
    return x->val + g_add;
}

inline int AskBlock(Heap *x)
{
    Heap *fx = Find(x);
    return fx->val + g_add;
}

inline int AskAll()
{
    multiset<int>::iterator it = G.end();
    return *(--it) + g_add;
}

int points,asks;
char s[10];

int main()
{
    //freopen("a.in","r",stdin);
    //freopen("a.out","w",stdout);
    cin >> points;
    for(int x,i = 1; i <= points; ++i) {
        scanf("%d",&x);
        heap[i] = Heap(x);
        G.insert(x);
    }
    cin >> asks;
    for(int x,y,i = 1; i <= asks; ++i) {
        scanf("%s",s);
        if(s[0] == ‘U‘) {
            scanf("%d%d",&x,&y);
            Heap *fx = Find(&heap[x]),*fy = Find(&heap[y]);
            if(fx == fy)    continue;
            Union(fx,fy);
        }
        if(s[0] == ‘A‘) {
            if(s[1] == ‘1‘) {
                scanf("%d%d",&x,&y);
                PlusPoint(&heap[x],y);
            }
            if(s[1] == ‘2‘) {
                scanf("%d%d",&x,&y);
                PlusBlock(&heap[x],y);
            }
            if(s[1] == ‘3‘) {
                scanf("%d",&x);
                PlusAll(x);
            }
        }
        if(s[0] == ‘F‘) {
            if(s[1] == ‘1‘) {
                scanf("%d",&x);
                printf("%d\n",AskPoint(&heap[x]));
            }
            if(s[1] == ‘2‘) {
                scanf("%d",&x);
                printf("%d\n",AskBlock(&heap[x]));
            }
            if(s[1] == ‘3‘)
                printf("%d\n",AskAll());
        }
    }
    return 0;
}
时间: 2024-10-16 00:20:31

BZOJ 2333 SCOI 2011 棘手的操作 可并堆的相关文章

BZOJ 2333 SCOI2011 棘手的操作 可并堆套可并堆

题目大意:给定n个节点,每个节点有一个初始权值,维护以下操作: 1.合并两个联通块 2.某个点权值+x 3.某个点所在联通块权值+x 4.所有点权值+x 5.询问某个点的权值 6.询问某个点所在联通块的最大权值 7.询问所有点之间的最大权值 2333333333333333333333333333333333333333333333333333333333333 2333333333333333333333333333333333333333333333333333333333333 23333

[BZOJ 2333] 棘手的操作 可并堆

实现 1. 可并堆 1.1. 可并堆的树高并不是 O(log n) 的. 例如我来一个极端数据, 从前往后逐个插入:  n n-1 n-2 ... 5 4 3 2 1 1.2. c[N][2], par[N] 的维护技巧 1.2.1. 注意两者是绑在一起的, 维护一个的时候要同时维护另一个. 1.2.2. 注意是否存在儿子, 是否存在父亲, 否则 par[0] = x , 就会导致一系列可怕的问题. 2. multiset 对于 int 类型的变量 x , erase(x) 表示删除权值为 x

BZOJ 2330 SCOI 2011 糖果

2330: [SCOI2011]糖果 Time Limit: 10 Sec Memory Limit: 128 MB Description 幼儿园里有N个小朋友,lxhgww老师现在想要给这些小朋友们分配糖果,要求每个小朋友都要分到糖果.但是小朋友们也有嫉妒心,总是会提出一些要求,比如小明不希望小红分到的糖果比他的多,于是在分配糖果的时候,lxhgww需要满足小朋友们的K个要求.幼儿园的糖果总是有限的,lxhgww想知道他至少需要准备多少个糖果,才能使得每个小朋友都能够分到糖果,并且满足小朋友

BZOJ 2330 SCOI 2011 糖果 差分约束系统

题目大意:幼儿园老师给小盆友们发糖果.有5种要求,问老师最少需要准备多少糖果.如不能满足,输出-1. 思路:裸地差分约束系统,但是正向加边会T,需要反向加边. CODE: #include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAX 400010 using namespace std; int

【bzoj2333】 [SCOI2011]棘手的操作 可并堆+lazy标记

2016-05-31  21:45:41 题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2333 (学习了黄学长的代码 有如下操作: U x y: 加一条边,连接第x个节点和第y个节点 A1 x v: 将第x个节点的权值增加v A2 x v: 将第x个节点所在的连通块的所有节点的权值都增加v A3 v: 将所有节点的权值都增加v F1 x: 输出第x个节点当前的权值 F2 x: 输出第x个节点所在的连通块中,权值最大的节点的权值 F3: 输

【bzoj2333】[SCOI2011]棘手的操作 可并堆+STL-set

题目描述 有N个节点,标号从1到N,这N个节点一开始相互不连通.第i个节点的初始权值为a[i],接下来有如下一些操作: U x y: 加一条边,连接第x个节点和第y个节点 A1 x v: 将第x个节点的权值增加v A2 x v: 将第x个节点所在的连通块的所有节点的权值都增加v A3 v: 将所有节点的权值都增加v F1 x: 输出第x个节点当前的权值 F2 x: 输出第x个节点所在的连通块中,权值最大的节点的权值 F3: 输出所有节点中,权值最大的节点的权值 输入 输入的第一行是一个整数N,代

[bzoj 2333] 棘手的操作[SCOI2011]

2333: [SCOI2011]棘手的操作 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2538  Solved: 986[Submit][Status][Discuss] Description 有N个节点,标号从1到N,这N个节点一开始相互不连通.第i个节点的初始权值为a[i],接下来有如下一些操作: U x y: 加一条边,连接第x个节点和第y个节点 A1 x v: 将第x个节点的权值增加v A2 x v: 将第x个节点所在的连通块的所有

2333: [SCOI2011]棘手的操作[离线线段树]

2333: [SCOI2011]棘手的操作 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2325  Solved: 909[Submit][Status][Discuss] Description 有N个节点,标号从1到N,这N个节点一开始相互不连通.第i个节点的初始权值为a[i],接下来有如下一些操作: U x y: 加一条边,连接第x个节点和第y个节点 A1 x v: 将第x个节点的权值增加v A2 x v: 将第x个节点所在的连通块的所有

2333: [SCOI2011]棘手的操作[写不出来]

2333: [SCOI2011]棘手的操作 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1979  Solved: 772[Submit][Status][Discuss] Description 有N个节点,标号从1到N,这N个节点一开始相互不连通.第i个节点的初始权值为a[i],接下来有如下一些操作: U x y: 加一条边,连接第x个节点和第y个节点 A1 x v: 将第x个节点的权值增加v A2 x v: 将第x个节点所在的连通块的所有