bzoj 2333 [SCOI2011]棘手的操作 —— 可并堆

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2333

稍微复杂,参考了博客:http://hzwer.com/5780.html

用 set 维护全局的最大值就可以方便地删除和查询了;

大概就是写一堆关于可并堆的子函数吧;

这里还用了斜堆,但其实并不明白原因是什么...

斜堆和左偏树只有一点点不同,它不用 dis ,而是每次都交换左右儿子,随机地保证了复杂度?

要注意 solvetag 函数是要把跟 x 有关的所有 lzy 关系都处理掉,所以也要处理 x 到其儿子的;

还有 del 处的顺序,小心不要让 set 查询的东西为空。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
using namespace std;
int const maxn=3e5+5;
int n,m,a[maxn],ls[maxn],rs[maxn],fa[maxn],lzy[maxn],ad,sta[maxn],top;
multiset<int>st;
char ch[5];
int find(int x){while(fa[x])x=fa[x]; return x;}
void pushdown(int x)
{
    if(!lzy[x])return;
    if(ls[x])lzy[ls[x]]+=lzy[x],a[ls[x]]+=lzy[x];
    if(rs[x])lzy[rs[x]]+=lzy[x],a[rs[x]]+=lzy[x];
    lzy[x]=0;
}
int merge(int x,int y)
{
    if(!x||!y)return x+y;
    if(a[x]<a[y])swap(x,y);
    pushdown(x);
    rs[x]=merge(rs[x],y);
    fa[rs[x]]=x;
    swap(ls[x],rs[x]);
    return x;
}
void solvetag(int x)
{
//    x=fa[x];    //从 x 开始,使 x 对儿子没有 lzy 的关联 
    while(x)sta[++top]=x,x=fa[x];
    while(top)pushdown(sta[top]),top--;
}
int del(int x)
{
    solvetag(x);
    int f=fa[x],k=merge(ls[x],rs[x]);
    ls[x]=rs[x]=fa[x]=0;
    fa[k]=f;
    if(ls[f]==x)ls[f]=k;
    else rs[f]=k;
    return find(k);
}
int rd()
{
    int ret=0,f=1; char cc=getchar();
    while(cc<'0'||cc>'9'){if(cc=='-')f=-1; cc=getchar();}
    while(cc>='0'&&cc<='9')ret=(ret<<3)+(ret<<1)+cc-'0',cc=getchar();
    return ret*f;
}
int main()
{
    n=rd();
    for(int i=1;i<=n;i++)a[i]=rd(),st.insert(a[i]);
    m=rd();
    for(int i=1,x,y;i<=m;i++)
    {
        cin>>ch;
        if(ch[0]=='U')
        {
            x=rd(); y=rd();
            x=find(x); y=find(y);
            if(x==y)continue;//
            if(merge(x,y)==x)st.erase(st.find(a[y]));
            else st.erase(st.find(a[x]));
        }
        if(ch[0]=='A'&&ch[1]=='1')
        {
            x=rd(); y=rd();
            solvetag(x);
//            int u=del(x); a[x]+=y;
//            st.erase(st.find(a[u])); //如果 x 只有自己,则删除后为空! 则RE 
//            st.insert(a[merge(u,x)]);
            st.erase(st.find(a[find(x)]));
            a[x]+=y;
            st.insert(a[merge(x,del(x))]);
        }
        if(ch[0]=='A'&&ch[1]=='2')
        {
            x=rd(); y=rd();
            int u=find(x); lzy[u]+=y; a[u]+=y;
            st.erase(st.find(a[u]-y));
            st.insert(a[u]);
        }
        if(ch[0]=='A'&&ch[1]=='3')y=rd(),ad+=y;
        if(ch[0]=='F'&&ch[1]=='1')x=rd(),solvetag(x),printf("%d\n",a[x]+ad);
        if(ch[0]=='F'&&ch[1]=='2')x=rd(),printf("%d\n",a[find(x)]+ad);
        if(ch[0]=='F'&&ch[1]=='3')printf("%d\n",*(--st.end())+ad);
    }
    return 0;
}

但是左偏树也可以做,而且也许是 set 常数太大,两种做法用时相差无几(都很慢,5000ms+);

不过比斜堆多开了一个数组呢。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
using namespace std;
int const maxn=3e5+5;
int n,m,a[maxn],ls[maxn],rs[maxn],fa[maxn],lzy[maxn],ad,sta[maxn],top,dis[maxn];
multiset<int>st;
char ch[5];
int find(int x){while(fa[x])x=fa[x]; return x;}
void pushdown(int x)
{
    if(!lzy[x])return;
    if(ls[x])lzy[ls[x]]+=lzy[x],a[ls[x]]+=lzy[x];
    if(rs[x])lzy[rs[x]]+=lzy[x],a[rs[x]]+=lzy[x];
    lzy[x]=0;
}
int merge(int x,int y)
{
    if(!x||!y)return x+y;
    if(a[x]<a[y])swap(x,y);
    pushdown(x);
    rs[x]=merge(rs[x],y);
    fa[rs[x]]=x;
    if(dis[ls[x]]<dis[rs[x]])swap(ls[x],rs[x]);
    if(rs[x])dis[x]=dis[rs[x]]+1;
    else dis[x]=0;
    return x;
}
void solvetag(int x)
{
//    x=fa[x];    //从 x 开始,使 x 对儿子没有 lzy 的关联 
    while(x)sta[++top]=x,x=fa[x];
    while(top)pushdown(sta[top]),top--;
}
int del(int x)
{
    solvetag(x);
    int f=fa[x],k=merge(ls[x],rs[x]);
    ls[x]=rs[x]=fa[x]=dis[x]=0;
    fa[k]=f;
    if(ls[f]==x)ls[f]=k;
    else rs[f]=k;
    return find(k);
}
int rd()
{
    int ret=0,f=1; char cc=getchar();
    while(cc<'0'||cc>'9'){if(cc=='-')f=-1; cc=getchar();}
    while(cc>='0'&&cc<='9')ret=(ret<<3)+(ret<<1)+cc-'0',cc=getchar();
    return ret*f;
}
int main()
{
    n=rd();
    for(int i=1;i<=n;i++)a[i]=rd(),st.insert(a[i]);
    m=rd();
    for(int i=1,x,y;i<=m;i++)
    {
        cin>>ch;
        if(ch[0]=='U')
        {
            x=rd(); y=rd();
            x=find(x); y=find(y);
            if(x==y)continue;//
            if(merge(x,y)==x)st.erase(st.find(a[y]));
            else st.erase(st.find(a[x]));
        }
        if(ch[0]=='A'&&ch[1]=='1')
        {
            x=rd(); y=rd();
            solvetag(x);
//            int u=del(x); a[x]+=y;
//            st.erase(st.find(a[u])); //如果 x 只有自己,则删除后为空! 则RE 
//            st.insert(a[merge(u,x)]);
            st.erase(st.find(a[find(x)]));
            a[x]+=y;
            st.insert(a[merge(x,del(x))]);
        }
        if(ch[0]=='A'&&ch[1]=='2')
        {
            x=rd(); y=rd();
            int u=find(x); lzy[u]+=y; a[u]+=y;
            st.erase(st.find(a[u]-y));
            st.insert(a[u]);
        }
        if(ch[0]=='A'&&ch[1]=='3')y=rd(),ad+=y;
        if(ch[0]=='F'&&ch[1]=='1')x=rd(),solvetag(x),printf("%d\n",a[x]+ad);
        if(ch[0]=='F'&&ch[1]=='2')x=rd(),printf("%d\n",a[find(x)]+ad);
        if(ch[0]=='F'&&ch[1]=='3')printf("%d\n",*(--st.end())+ad);
    }
    return 0;
}

 

posted @ 2018-09-07 00:12  Zinn  阅读(140)  评论(0编辑  收藏  举报