bzoj 2333 [SCOI2011]棘手的操做 —— 可並堆

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

稍微複雜,參考了博客:http://hzwer.com/5780.htmlhtml

用 set 維護全局的最大值就能夠方便地刪除和查詢了;ios

大概就是寫一堆關於可並堆的子函數吧;數組

這裏還用了斜堆,但其實並不明白緣由是什麼...函數

斜堆和左偏樹只有一點點不一樣,它不用 dis ,而是每次都交換左右兒子,隨機地保證了複雜度?spa

要注意 solvetag 函數是要把跟 x 有關的全部 lzy 關係都處理掉,因此也要處理 x 到其兒子的;code

還有 del 處的順序,當心不要讓 set 查詢的東西爲空。htm

代碼以下:blog

#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+);ci

不過比斜堆多開了一個數組呢。

代碼以下:

#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;
}
相關文章
相關標籤/搜索