1. 程式人生 > >BZOJ4399魔法少女LJJ——線段樹合並+並查集

BZOJ4399魔法少女LJJ——線段樹合並+並查集

gem 合並 long esp pan 斷開 ostream 多少 ios

題目描述

在森林中見過會動的樹,在沙漠中見過會動的仙人掌過後,魔法少女LJJ已經覺得自己見過世界上的所有稀奇古怪的事情了
LJJ感嘆道“這裏真是個迷人的綠色世界,空氣清新、淡雅,到處散發著醉人的奶漿味;小猴在枝頭悠來蕩去,好不自在;各式各樣的鮮花爭相開放,各種樹枝的枝頭掛滿沈甸甸的野果;鳥兒的歌聲婉轉動聽,小河裏飄著落下的花瓣真是人間仙境”
SHY覺得LJJ還是太naive,一天,SHY帶著自己心愛的圖找到LJJ,對LJJ說:“既然你已經見識過動態樹,動態仙人掌了,那麽今天就來見識一下動態圖吧”
LJJ:“要支持什麽操作?”

SHY:“
1.新建一個節點,權值為x。
2.連接兩個節點。
3.將一個節點a所屬於的聯通快內權值小於x的所有節點權值變成x。
4.將一個節點a所屬於的聯通快內權值大於x的所有節點權值變成x。
5.詢問一個節點a所屬於的聯通塊內的第k小的權值是多少。
6.詢問一個節點a所屬聯通快內所有節點權值之積與另一個節點b所屬聯通快內所有節點權值之積的大小。
7.詢問a所在聯通快內節點的數量
8.若兩個節點a,b直接相連,將這條邊斷開。
9.若節點a存在,將這個點刪去。

LJJ:“我可以離線嗎?”
SHY:“可以,每次操作是不加密的,”
LJJ:“我可以暴力嗎?”
SHY:“自重”
LJJ很郁悶,你能幫幫他嗎

輸入

第一行有一個正整數m,表示操作個數。
接下來m行,每行先給出1個正整數c。
若c=1,之後一個正整數x,表示新建一個權值為x的節點,並且節點編號為n+1(當前有n個節點)。
若c=2,之後兩個正整數a,b,表示在a,b之間連接一條邊。
若c=3,之後兩個正整數a,x,表示a聯通快內原本權值小於x的節點全部變成x。
若c=4,之後兩個正整數a,x,表示a聯通快內原本權值大於x的節點全部變成x。
若c=5,之後兩個正整數a,k,表示詢問a所屬於的聯通塊內的第k小的權值是多少。
若c=6,之後兩個正整數a,b,表示詢問a所屬聯通快內所有節點權值之積與b所屬聯通快內所有節點權值之積的大小,

若a所屬聯通快內所有節點權值之積大於b所屬聯通快內所有節點權值之積,輸出1,否則為0。
若c=7,之後一個正整數a,表示詢問a所在聯通塊大小
若c=8,之後兩個正整數a,b,表示斷開a,b所連接的邊。
若c=9,之後一個正整數a,表示斷開a點的所有連邊
具體輸出格式見樣例

輸出

樣例輸入

11
1 2
1 3
1 4
1 5
1 6
2 1 2
2 2 3
2 3 4
2 4 5
3 2 5
5 3 4

樣例輸出

5

提示

對100%的數據 0<=m<=400000,c<=7,所有出現的數均<=1000000000,所有出現的點保證存在

【HINT】請認真閱讀題面

剛讀完題面可能會覺得這道題不可做,8、9操作怎麽搞?但再往下看看數據範圍c<=7,根本不存在後兩個操作!

所以原題樣例也就修改成了上面的這個樣例。這樣用線段樹合並+並查集就能做了。

我們來分別說說每個操作:

1、直接建一個點,並建一棵這個點所代表的權值線段樹(別忘了動態開點哦!)

2、如果這兩個點在同一棵聯通塊中這個操作就沒用了,因為詢問只詢問聯通塊信息,否則把這兩個點所在的聯通塊合並,並把兩個聯通塊的祖先所代表的權值線段樹合並

3、直接找到a聯通塊祖先的線段樹,區間修改就好了,具體見代碼。

4、實現同上。

5、還是找到聯通塊祖先的權值線段樹查詢第k小。

6、正常思路是維護線段樹區間乘積,然後直接查詢a,b聯通塊祖先線段樹中根節點的權值乘積就好了,但發現乘積太大了,因此考慮轉成log。因為log(x*y)=logx+logy,所以每個點權值取log然後維護區間和即可,精度在double下能過。

7、如果用啟發式合並直接輸出聯通塊大小即可,不啟發式合並還要在線段樹上維護區間數的個數。

#include<set>
#include<map>
#include<stack>
#include<queue>
#include<cmath>
#include<cstdio>
#include<vector>
#include<bitset>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
int f[400010];
int root[400010];
int cnt;
int size[400010];
int ls[7600010];
int rs[7600010];
bool a[7600010];
double g[7600010];
int sum[7600010];
int n;
int opt;
int x,y;
int num;
int find(int x)
{
    if(f[x]==x)
    {
        return x;
    }
    return f[x]=find(f[x]);
}
void pushup(int rt)
{
    sum[rt]=sum[ls[rt]]+sum[rs[rt]];
    g[rt]=g[ls[rt]]+g[rs[rt]];
}
void pushdown(int rt)
{
    if(a[rt])
    {
        a[ls[rt]]=1;
        a[rs[rt]]=1;
        sum[ls[rt]]=0;
        g[ls[rt]]=0;
        sum[rs[rt]]=0;
        g[rs[rt]]=0;
        a[rt]=0;
    }
}
void insert(int &rt,int l,int r,int k)
{
    if(!rt)
    {
        rt=++cnt;
    }
    if(l==r)
    {
        sum[rt]++;
        g[rt]+=log(l);
        return ;
    }
    int mid=(l+r)>>1;
    if(k<=mid)
    {
        insert(ls[rt],l,mid,k);
    }
    else
    {
        insert(rs[rt],mid+1,r,k);
    }
    pushup(rt);
}
void changemin(int &rt,int l,int r,int k,int x)
{
    if(!rt)
    {
        rt=++cnt;
    }
    if(l==r)
    {
        sum[rt]+=x;
        g[rt]+=log(l)*x;
        return ;
    }
    int mid=(l+r)>>1;
    pushdown(rt);
    if(k<=mid)
    {
        changemin(ls[rt],l,mid,k,x);
    }
    else
    {
        x+=sum[ls[rt]];;
        g[ls[rt]]=0;
        sum[ls[rt]]=0;
        a[ls[rt]]=1;
        changemin(rs[rt],mid+1,r,k,x);
    }
    pushup(rt);
}
void changemax(int &rt,int l,int r,int k,int x)
{
    if(!rt)
    {
        rt=++cnt;
    }
    if(l==r)
    {
        sum[rt]+=x;
        g[rt]+=log(l)*x;
        return ;
    }
    int mid=(l+r)>>1;
    pushdown(rt);
    if(k<=mid)
    {
        x+=sum[rs[rt]];
        sum[rs[rt]]=0;
        g[rs[rt]]=0;
        a[rs[rt]]=1;
        changemax(ls[rt],l,mid,k,x);
    }
    else
    {
        changemax(rs[rt],mid+1,r,k,x);
    }
    pushup(rt);
}
int query(int rt,int l,int r,int k)
{
    if(l==r)
    {
        return l;
    }
    int mid=(l+r)>>1;
    pushdown(rt);
    if(sum[ls[rt]]>=k)
    {
        return query(ls[rt],l,mid,k);
    }
    else
    {
        return query(rs[rt],mid+1,r,k-sum[ls[rt]]);
    }
}
void merge(int &rt,int x)
{
    if(!rt||!x)
    {
        rt=rt+x;
        return ;
    }
    pushdown(rt);
    pushdown(x);
    sum[rt]+=sum[x];
    g[rt]+=g[x];
    merge(ls[rt],ls[x]);
    merge(rs[rt],rs[x]);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&opt);
        if(opt==1)
        {
            scanf("%d",&x);
            num++;
            f[num]=num;
            size[num]=1;
            insert(root[num],1,1000000000,x);
        }
        else if(opt==2)
        {
            scanf("%d%d",&x,&y);
            int fx=find(x);
            int fy=find(y);
            if(fx!=fy)
            {
                if(size[fx]>size[fy])
                {
                    size[fx]+=size[fy];
                    f[fy]=fx;
                    merge(root[fx],root[fy]);
                }
                else
                {
                    size[fy]+=size[fx];
                    f[fx]=fy;
                    merge(root[fy],root[fx]);
                }
            }
        }
        else if(opt==3)
        {
            scanf("%d%d",&x,&y);
            x=find(x);
            changemin(root[x],1,1000000000,y,0);
        }
        else if(opt==4)
        {
            scanf("%d%d",&x,&y);
            x=find(x);
            changemax(root[x],1,1000000000,y,0);
        }
        else if(opt==5)
        {
            scanf("%d%d",&x,&y);
            x=find(x);
            printf("%d\n",query(root[x],1,1000000000,y));
        }
        else if(opt==6)
        {
            scanf("%d%d",&x,&y);
            x=find(x);
            y=find(y);
            if(g[root[x]]>g[root[y]])
            {
                printf("1\n");
            }
            else
            {
                printf("0\n");
            }
        }
        else if(opt==7)
        {
            scanf("%d",&x);
            x=find(x);
            printf("%d\n",size[x]);
        }
    }
}

BZOJ4399魔法少女LJJ——線段樹合並+並查集