bzoj2333 [SCOI2011]棘手的操作

6 篇文章 0 订阅
4 篇文章 0 订阅

传送门

事实证明:同时维护并查集和左偏树里的父节点是完全错误的……
题目的实质是要求可并堆(我用的左偏树)支持删除任意点,然后自己就yy了一种删除操作,大致就是把要删除的点先加上所有祖先节点上的标记,然后把它的左右子树合并加到原来的父节点对应的儿子位置上,同时维护父亲。
题目还有一个操作是找所有节点中权值最大的那个,我一开始写了另一个左偏树来维护,结果搞了好久都没调出来,看了黄学长的题解发现STL的妙用于是秒改。。其实这个时候把并查集删掉就能A了。然而我没删,结果每次都RE,GG

总结:
并查集和父节点不要同时维护;(这种暴力查找父节点的复杂度应该和不路径压缩和按秩合并的并查集差不多,但是并不会T,我也不知道为什么,如果有好心人可以在底下留言告诉我,感激不尽)
STL有时能大大简化代码复杂度,一定要灵活使用!

CODE:

#include<set>
#include<cstdio>
#include<iostream>
using namespace std;
const int N=3e5+10;
struct node
{
    int num,plus,dis,id;
    node *ch[2],*fa;
    inline void pushdown();
}pool[N],*t[N],*null;
inline void node::pushdown()
{
    if(plus)
    {
        if(ch[0]!=null) ch[0]->plus+=plus,ch[0]->num+=plus;
        if(ch[1]!=null) ch[1]->plus+=plus,ch[1]->num+=plus;
        plus=0;
    }
}
int n,m,x,y,tot,Plus;
multiset<int> s;
inline void getnew(int value,int id)
{
    node *now=pool+ ++tot;
    now->ch[0]=now->ch[1]=now->fa=null;
    now->num=value,now->id=id;
    now->dis=1;
    t[id]=now;
}
inline int find(int x)
{
    while(t[x]->fa!=null) x=t[x]->fa->id;
    return x;
}
int findtag(node *now)
{
    if(now==null) return 0;
    return findtag(now->fa)+now->plus;
}
node *merge(node *x,node *y)
{
    if(x==null) return y;
    if(y==null) return x;
    if(x->num<y->num) swap(x,y);
    x->pushdown(),y->pushdown();
    x->ch[1]=merge(x->ch[1],y);
    x->ch[1]->fa=x;
    if(x->ch[0]->dis<x->ch[1]->dis) swap(x->ch[0],x->ch[1]);
    x->dis=x->ch[1]->dis+1;
    return x;
}
inline node *del(node *now)
{
    node *fa=now->fa,*child=merge(now->ch[0],now->ch[1]);
    now->ch[0]=now->ch[1]=now->fa=null;
    if(fa->ch[0]==now) fa->ch[0]=child;
    else fa->ch[1]=child;
    child->fa=fa;
    return t[find(child->id)];
}
inline void Merge(int x,int y)
{
    x=find(x),y=find(y);
    if(x==y) return;
    if(merge(t[x],t[y])==t[x]) s.erase(s.find(t[y]->num));
    else s.erase(s.find(t[x]->num));
}
inline void change(int x,int y)
{
    node *now=t[x];s.erase(s.find(t[find(x)]->num));
    now->num+=y+findtag(now->fa);now->pushdown();
    s.insert(merge(del(now),now)->num);
}
inline void add(node *root,int y)
{
    s.erase(s.find(root->num));
    root->plus+=y,root->num+=y;
    s.insert(root->num);
}
int main()
{
    null=pool;
    null->ch[0]=null->ch[1]=null->fa=null;
    null->dis=-1;t[0]=null;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
      scanf("%d",&x),getnew(x,i),s.insert(x);
    scanf("%d",&m);
    while(m--)
    {
        char c=getchar();
        while(c<'A'||c>'Z') c=getchar();
        if(c=='A')
        {
            c=getchar();
            if(c=='1') scanf("%d%d",&x,&y),change(x,y);
            else if(c=='2') scanf("%d%d",&x,&y),add(t[find(x)],y);
            else scanf("%d",&x),Plus+=x;
        }
        else if(c=='F')
        {
            c=getchar();
            if(c=='1') scanf("%d",&x),printf("%d\n",t[x]->num+findtag(t[x]->fa)+Plus);
            else if(c=='2') scanf("%d",&x),printf("%d\n",t[find(x)]->num+Plus);
            else printf("%d\n",*--s.end()+Plus);
        }
        else scanf("%d%d",&x,&y),Merge(x,y);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值