BZOJ 3673 可持久化並查集 by zky 可持續化線段樹+並查集啟發式合併
題目
BZOJ 3673
LUOGU 3402
Description
n個集合 m個操作
操作:
1 a b 合併a,b所在集合
2 k 回到第k次操作之後的狀態(查詢算作操作)
3 a b 詢問a,b是否屬於同一集合,是則輸出1否則輸出0
0<n,m<=2*10^4
Input
Output
Sample Input
5 6
1 1 2
3 1 2
2 0
3 1 2
2 1
3 1 2
Sample Output
1
0
1
HINT
Source
題解
-
這題不知道出題人什麼做法,但是程式碼很短的樣子
UPD:出題人用的是rope,即stl中的可持久化平衡樹
KuribohG神犇告訴了我可以用可持久化線段樹實現可持久化陣列T T
既然都有可持久化陣列了,只要用個再並查集的啟發式合併就能妥妥的水過了(這樣每次只要修改一個fa)。 -
並查集的啟發式合併就是按秩合併,初始所有集合秩為0
合併把秩小的樹根的父親設為秩大的樹根
如果秩相同,則隨便選取一個作為父節點並將它的秩+1
秩和fa一樣維護。 -
但是其實這題資料隨機的話隨便合併就行了,根本不用按秩合併什麼的
UPD:秩其實有的時候很不好用,維護子樹大小比較贊。。。
另外,ndsf發現只要直接暴力就能虐了T T。
引用自hzwer
程式碼
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
template<typename T>inline void read(T &x)
{
x=0;
T f=1,ch=getchar();
while (!isdigit(ch) && ch^'-') ch=getchar();
if (ch=='-') f=-1, ch=getchar();
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
x*=f;
}
struct rec
{
int l,r;
}tree[maxn*30];
int Ed[maxn],tot;//Ed[]是版本號,tot是節點總數(這些就是主席樹啦)
int n,m,fa[maxn*30],deep[maxn*30];//deep[]存最大深度,fa[]存一個點在某個版本的父親
inline void build(int &now,int l,int r)
{
now=++tot;
if (l==r)
{
fa[now]=l;//初始版本:父親是自己,就像並查集初始化每個點的父親是它自己
return ;
}
int mid=(l+r)>>1;
build(tree[now].l,l,mid);
build(tree[now].r,mid+1,r);
}
//主席樹維護的是:每一個版本,每一個點的父親是誰
inline void update(int &now,int last,int l,int r,int x,int f)//把x的父親改成f
{
now=++tot;
if (l==r)
{
fa[now]=f;
deep[now]=deep[last];
return ;
}
tree[now]=tree[last];//deep[]用於啟發式合併
int mid=(l+r)>>1;
if (x<=mid)
update(tree[now].l,tree[last].l,l,mid,x,f);
else
update(tree[now].r,tree[last].r,mid+1,r,x,f);
}
inline int query(int now,int l,int r,int x)//詢問某一個版本的一個點的父親
{
if (l==r)
return now;
int mid=(l+r)>>1;
if (x<=mid)
return query(tree[now].l,l,mid,x);
else
return query(tree[now].r,mid+1,r,x);
}
inline void add(int now,int l,int r,int x)//把某一個並查集聯通塊中每一個點的深度加一
{
if (l==r)
{
++deep[now];
return ;
}
int mid=(l+r)>>1;
if (x<=mid)
add(tree[now].l,l,mid,x);
else
add(tree[now].r,mid+1,r,x);
}
inline int get(int ed,int x)//ed 版本編號
{
register int f=query(ed,1,n,x);//查詢在這一版本里 一個點的父親
if (x==fa[f]) return f;
return get(ed,fa[f]);//不帶路徑壓縮的並查集
}
int main()
{
read(n);read(m);
build(Ed[0],1,n);
for (register int opt,k,a,b,i=1;i<=m;++i)
{
read(opt);
if (opt==1)
{
Ed[i]=Ed[i-1];
read(a);read(b);
register int f1=get(Ed[i],a);
register int f2=get(Ed[i],b);
if (fa[f1]==fa[f2]) continue;
if (deep[f1] > deep[f2])
swap(f1,f2);//把大的往小的並,保證f1兒子節點數一定是小於等於f2
update(Ed[i],Ed[i-1],1,n,fa[f1],fa[f2]);
if (deep[f1]==deep[f2])
add(Ed[i],1,n,fa[f2]);//因為f2併到了f1,所以f1的深度要加1
//我們用啟發式合併來保證並查集合並的複雜度
}
else if (opt==2)
{
read(k);
Ed[i]=Ed[k];
}
else
{
Ed[i]=Ed[i-1];
read(a);read(b);
register int f1=get(Ed[i],a);
register int f2=get(Ed[i],b);
if (fa[f1]==fa[f2]) puts("1");
else puts("0");
}
}
return 0;
}
相關文章
- BZOJ3673 : 可持久化並查集持久化並查集
- 【題解】Solution Set - NOIP2024集訓Day8 並查集和可持久化並查集並查集持久化
- 可持久化線段樹持久化
- USACO 2020 OPEN Favorite Colors【並查集-啟發式合併-思考】並查集
- 「學習筆記」可持久化線段樹筆記持久化
- BZOJ 4195 程式自動分析【並查集+離散化】並查集
- 並查集到帶權並查集並查集
- 【資料結構】可持久化線段樹初步資料結構持久化
- P3834 【模板】可持久化線段樹 2持久化
- 區間k小值(可持久化線段樹)持久化
- 並查集系列之「思路優化」並查集優化
- 演算法隨筆——主席樹(可持久化線段樹)演算法持久化
- 【並查集】【帶偏移的並查集】食物鏈並查集
- 可持久化線段————主席樹(洛谷p3834)持久化
- hdu 2665 可持久化線段樹求區間第K大值(函式式線段樹||主席樹)持久化函式
- 並查集系列之「思路最佳化」並查集
- 【主席樹】P3919 【模板】可持久化線段樹 1持久化
- bzoj3444: 最後的晚餐(並查集+組合數學)並查集
- 並查集(一)並查集的幾種實現並查集
- 【BZOJ3207】花神的嘲諷計劃Ⅰ hash+可持久化線段樹持久化
- 使用並查集處理集合的合併和查詢問題並查集
- 3.1並查集並查集
- 並查集(小白)並查集
- 並查集的一些基本概念以及基本操作(初始化,合併,查詢等操作)並查集
- 樹(tree) - 題解(帶權並查集)並查集
- Codeforces 455C Civilization:樹的直徑 + 並查集【合併樹後直徑最小】並查集
- 排序(並查集&&雜湊函式)排序並查集函式
- 藍橋杯-並查集-合根植物並查集
- 並查集(Union Find)並查集
- 並查集應用並查集
- The Door Problem 並查集並查集
- 並查集練習並查集
- 並查集的使用並查集
- 並查集—應用並查集
- 寫模板, 並查集。並查集
- 並查集跳躍並查集
- 各種並查集並查集
- 食物鏈(並查集)並查集