【BZOJ2333】【SCOI2011】棘手的操作 可并堆+堆套堆(什么嘛,用个set就好啦)

链接:

#include <stdio.h>
int main()
{
    puts("转载请注明出处[vmurder]谢谢");
    puts("网址:blog.csdn.net/vmurder/article/details/44525405");
}

可并堆

我写的是随机堆,然后模板可见上一篇博客。
【BZOJ1455】罗马游戏 可并堆
http://blog.csdn.net/vmurder/article/details/44513511

解一下题意:

原始题意:

有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:

输出所有节点中,权值最大的节点的权值

可并堆中操作:

U :

利用可并堆的merge操作合并两堆,需要判重跳出操作。

A1 :

二叉堆版:

从节点 x 向上判断一下大小更新一下堆。
然后从节点x向下判断一下大小更新一下堆。
向上了不起三四十行,细节,咳咳,有点多,但也不是不能写,但是向下更新确实有点醉啊。
蒟蒻写不起,但是可以给你们看一下我开始写的向上更新(没写向下所以WA了)。

代码(算上整体处理37行):
    void A1()
    {
        int x,y;
        scanf("%d%d",&x,&y),val[x]+=y;
        pushdown(x);
        if(root[x]==x)
        {
            H.change(x,val[x]);
            return ;
        }
        int last;
        for(y=fa[x];y;y=fa[x])
        {
            if(val[x]<=val[y])return ;

            int i=(x==RS),t=son[y][!i];
            pushdown(y),pushdown(x);

            fa[x]=fa[y],son[fa[y]][y==son[fa[y]][1]]=x;



            LS=ls,RS=rs;
            if(ls)fa[ls]=y;
            if(rs)fa[rs]=y;

            fa[y]=x,son[x][i]=y;

            son[x][!i]=t;if(t)fa[t]=x;
            last=y;
        }
        root[x]=root[last]=x;
        pushdown(x);

        H.remove(last);
        H.insert(val[x],x);
可并堆版:

(⊙ω⊙) 先蹲掩体后面来包辣条压压惊。

那个吧,就是把 x 的两个子节点,还有它的父亲节点都搞出来,其中注意标记的下传,然后暴力用merge函数合并一下,时间复杂度是期望O(log2n)的哦亲。

注意一下没有儿子啊,没有父亲啊神马的之类的细节问题就好啦。
有一个剪枝:就是判断一下如果加了权值之后不改变树构,就加完然后跳出操作。(实测这个剪枝没什么意义。

A2 :

x 的堆顶加个标记。

A3 :

加个全局标记,输出神马乱七八糟的的时候加上去就好啦。

F1 :

先把到堆顶的一串点的标记传下来,然后直接输出点权就好啦。
嗯,期望时间复杂度O(log2n)。诶那个 n 就是n,因为随机数据让图很快就都连一起了。

F2 :

输出 x 的堆顶元素权值。

F3 :

输出所有堆顶中的最大值,这个可以额外用一个堆维护一下,就是传说中的堆套堆,但是跟什么对顶堆不是一个东西啦(Orz PoPoQQQ大爷)。
当然再写个堆多闹心,来个multiset搞一搞就行啦,题能过就行,时间什么的不要太在意啦。

对于每个堆顶:

这个堆顶怎么维护呢?

U :

删除其中一个,具体操作是最后看哪个依然是堆顶,就删除另一个。
multiset 中是Heap.erase(Heap.find(val[???]));

A1 :

先把可并堆堆顶元素从裸堆中删掉,然后都处理完了再加回去。
加入是Heap.insert(val[???]);

A2 :

把堆顶元素权值删了再加,或者你写堆的话就把堆中每个点映射一下,然后修改这个点,向上向下分别比较大小维护一下堆性质就行了。
multiset 中的俩操作都一样了。

代码:

#include <set>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 301000
#define inf 0x3f3f3f3f
#define ls son[x][0]
#define rs son[x][1]
#define LS son[y][0]
#define RS son[y][1]
using namespace std;
multiset<int>H;
struct Mergeable_Heap
{
    int son[N][2],val[N],flag[N],FLAG;

    int root[N],fa[N];
    int find(int x)
    {
        if(root[x]==x)return x;
        return root[x]=find(root[x]);
    }

    int stk[N],top;

    void init(int n)
    {
        val[0]=-inf;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&val[i]);
            H.insert(val[i]);
            root[i]=i;
        }
    }
    void pushdown(int x)
    {
        val[x]+=flag[x];
        if(ls)flag[ls]+=flag[x];
        if(rs)flag[rs]+=flag[x];
        flag[x]=0;
    }
    int merge(int x,int y)
    {
        pushdown(x),pushdown(y);
        if(val[x]<val[y])swap(x,y);
        if((x&&y)==0)return x;
        int d=rand()&1;
        son[x][d]=merge(son[x][d],y);
        if(son[x][d])fa[son[x][d]]=x;
        return x;
    }
    void U()
    {
        int x,y;
        scanf("%d%d",&x,&y);
        int fx=find(x),fy=find(y);
        if(fx==fy)return ;
        root[fx]=root[fy]=merge(fx,fy);

        if(root[fx]==fx)H.erase(H.find(val[fy]));
        else H.erase(H.find(val[fx]));
    }
    void A1()
    {
        int x,y,A,B;
        scanf("%d%d",&x,&A);

/*      if(val[x]+A>=max(val[ls],val[rs]))
        {
            pushdown(x);
            if(fa[x]&&val[fa[x]]>=val[x]+flag[x]+A)
            {
                val[x]+=A;
                return ;
            }
        }
*/
        for(stk[top=1]=x,y=fa[x];y;y=fa[y])stk[++top]=y;
            int head=stk[top];
        H.erase(H.find(val[head]));
        while(top)pushdown(stk[top--]);
        val[x]+=A;

        y=fa[x];
        int i=(x==son[y][1]);
        son[y][i]=fa[x]=0;

        A=ls,B=rs;
        fa[A]=fa[B]=0;
        ls=rs=0;

        A=merge(A,B);
        if(A)fa[A]=y;
        if(y)son[y][i]=A,A=head;

        int last=head;
        root[A]=root[x]=root[head]=merge(x,A);
        root[0]=0;
        pushdown(root[x]);
        H.insert(val[root[x]]);
    }
    void A2()
    {
        int x,y;
        scanf("%d%d",&x,&y);
        H.erase(H.find(val[find(x)]));
        flag[find(x)]+=y;
        pushdown(root[x]);
        H.insert(val[root[x]]);
    }
    void A3()
    {
        int x;
        scanf("%d",&x);
        FLAG+=x;
    }
    void F1()
    {
        int x,y;
        scanf("%d",&x);
        pushdown(x);
        if(x==root[x])
        {
            printf("%d\n",val[x]+FLAG);
            return ;
        }
        for(stk[top=1]=x,y=fa[x];y;y=fa[y])stk[++top]=y;
        while(top)pushdown(stk[top--]);

        printf("%d\n",val[x]+FLAG);
    }
    void F2()
    {
        int x;
        scanf("%d",&x);
        pushdown(find(x));
        printf("%d\n",val[root[x]]+FLAG);
    }
    void F3()
    {
        multiset<int>::iterator it = H.end();
        printf("%d\n",(*(--it))+FLAG);
    }
}heap;
int n,m;
char s[5];
int main()
{
//  freopen("test.in","r",stdin);
    srand(252503);

    int i,j,k;
    scanf("%d",&n);
    heap.init(n);

    for(scanf("%d",&m);m--;)
    {
        scanf("%s",s);
        if(s[0]=='U')heap.U();
        else if(s[0]=='A'&&s[1]=='1')heap.A1();
        else if(s[0]=='A'&&s[1]=='2')heap.A2();
        else if(s[0]=='A'&&s[1]=='3')heap.A3();
        else if(s[0]=='F'&&s[1]=='1')heap.F1();
        else if(s[0]=='F'&&s[1]=='2')heap.F2();
        else if(s[0]=='F'&&s[1]=='3')heap.F3();
    }
    return 0;
}

/*
2015.3.21.22:42

4
-594 -709 425 563
12
A1 3 -274
U 1 2
U 1 4
A2 1 599
A1 1 218
A1 2 777
A1 1 -300
A2 1 -877
A2 1 35
A1 4 80
U 1 3
F2 3
*/

数据生成器:

还是蛮靠谱的,我调这么长时间一直靠的它~

#include <ctime>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3fll
using namespace std;
long long RAND(long long L=0,long long R=INF)
{
    if(L>R)swap(L,R);
    return (long long)rand()*rand()*rand()*rand()%(R-L+1)+L;
}
int Rand(int L=0,int R=inf){return (int)(RAND((long long)L,(long long)R));}
int main()
{
//  freopen("test.in","w",stdout);
    srand((unsigned)time(NULL));

    int i,j,k;
    int a,b,c;
    int n,m,p;

    n=10,m=100;
    printf("%d\n",n);
    for(i=1;i<=n;i++)printf("%d ",Rand(-1000,1000));
        puts("");
    printf("%d\n",m);
    while(m--)
    {
        int gailv=rand()%7;
        if(gailv==0)
        {
            printf("U");
            do{a=Rand(1,n),b=Rand(1,n);}while(a==b);
            printf(" %d %d\n",a,b);
        }
        if(gailv==1)
        {
            printf("A1");
            printf(" %d %d\n",Rand(1,n),Rand(-1000,1000));
        }
        if(gailv==2)
        {
            printf("A2");
            printf(" %d %d\n",Rand(1,n),Rand(-1000,1000));
        }
        if(gailv==3)
        {
            printf("A3");
            printf(" %d\n",Rand(-1000,1000));
        }
        if(gailv==4)
        {
            printf("F1");
            printf(" %d\n",Rand(1,n));
        }
        if(gailv==5)
        {
            printf("F2");
            printf(" %d\n",Rand(1,n));
        }
        if(gailv==6)
        {
            printf("F3\n");
        }

    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值