zoukankan      html  css  js  c++  java
  • 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
    ",a[x]+ad);
            if(ch[0]=='F'&&ch[1]=='2')x=rd(),printf("%d
    ",a[find(x)]+ad);
            if(ch[0]=='F'&&ch[1]=='3')printf("%d
    ",*(--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
    ",a[x]+ad);
            if(ch[0]=='F'&&ch[1]=='2')x=rd(),printf("%d
    ",a[find(x)]+ad);
            if(ch[0]=='F'&&ch[1]=='3')printf("%d
    ",*(--st.end())+ad);
        }
        return 0;
    }
  • 相关阅读:
    Codeforces Round #657 (Div. 2) 题解
    洛谷 P2765 魔术球问题 (最小路径覆盖 or 贪心)
    洛谷 P2472 蜥蜴 (最大流)
    Codeforces Round #665 (Div. 2) 题解
    洛谷 P1231 教辅的组成 (三分图匹配,裂点)
    USACO5.4 奶牛的电信Telecowmunication (最小割,割边转割点)
    有关网络流的一些板子题
    洛谷 p2756 飞行员配对方案问题(最大流,二分图匹配)
    JSON.toJSONString中序列化空字符串遇到的坑
    关于mysql自动备份的小方法
  • 原文地址:https://www.cnblogs.com/Zinn/p/9600297.html
Copyright © 2011-2022 走看看