這兩天學習了一些新型的並查集,比如帶權並查集,種類並查集等。
眾所周知,並查集就是記錄了元素和元素之間的關係,主要是歸屬關係,可以把一部分元素分類到其所屬的集合中,而帶權並查集,維護的就不只是關係了,還有元素和元素之間的權值差。如果將乙個並查集模擬成一張圖,那麼所謂帶權,就是指邊權或者點權。
這是乙個簡略的帶權並查集,1就是這個集合的頭,帶圈的數字就是元素,其他數字就是對應的權值,將其轉化為點權的話,就是val[1]=0,val[2]=1,val[3]=1,val[4]=2,有點像字首和的感覺。
那麼這種並查集有什麼用的,很顯著的乙個特點,這種並查集可以存下元素和元素之間的相對關係,而原先的並查集只能儲存歸屬關係。來一道例題幫助一下理解吧。
hdu-3038-how many answers are wrong
有m個數,不知道它們具體的值,但是知道某兩個數之間(包括這兩個數)的所有數之和,現在給出n個這樣的區間和資訊,需要判斷有多少個這樣的區間和與前邊已知的區間和存在矛盾。例如給出區間和[1,4]為20,[3,4]為15,再給出[1,2]為30,顯然這個[1,2]的值就有問題,它應該為20-15=5。
如果用帶權並查集來做這道題,首先,我們知道,帶權並查集是儲存了乙個並查集中元素之間的相對關係,那麼我們就可以利用這些元素之間的相對關係得到這個集合中任意兩個元素之間的關係,模擬到這道題中,就是查詢的區間是否衝突。如果查詢的區間是在乙個集合中的,那麼我們就可以知道這個區間的相對差值,如果和給的值不同,自然就是衝突的。
#include
#include
#include
#include
#include
#include
#include
#include
#include
using
namespace std;
typedef
long
long ll;
#define pb push_back
#define mk make_pair
#define rep(a,b,c) for(int a=b;a#define repp(a,b,c) for(int a=b;a<=c;a++)
#define mem(a,b) memset(a,b,sizeof(a))
const
int inf=
0x3f3f3f3f
;void
useiostream()
const
int maxn=
200005
;int root[maxn]
;int val[maxn]
;int
find
(int num)
return root[num];}
intmain()
}else
} cout<
}}
做過這道題,我對帶權並查集有了乙個模糊的認識,所謂帶權,就是處理集合內元素之間的關係,其難點在於,如何將這些元素的關係用字首的形式表現出來,這是路徑壓縮和集合合併的關鍵。
再做一道題熟悉一下。
hihocoder 1515 分數調查
中文題意我就不描述了,和上面那道題挺像的。
#include
#include
#include
#include
#include
#include
#include
#include
#include
using
namespace std;
typedef
long
long ll;
#define pb push_back
#define mk make_pair
#define rep(a,b,c) for(int a=b;a#define repp(a,b,c) for(int a=b;a<=c;a++)
#define mem(a,b) memset(a,b,sizeof(a))
const
int inf=
0x3f3f3f3f
;const
int maxn=
100005
;int root[maxn]
;int val[maxn]
;void
useiostream()
intfind
(int num)
return root[num];}
voidun(
int a,
int b,
int c)
}int
main()
rep(i,
0,q)
else}}
}
第三道題挺有意思的,算是帶權並查集的乙個變種應用
p2024 [noi2001]食物鏈
題目描述
動物王國中有三類動物 a,b,c,這三類動物的食物鏈構成了有趣的環形。a 吃 b,b
吃 c,c 吃 a。
現有 n 個動物,以 1 - n 編號。每個動物都是 a,b,c 中的一種,但是我們並不知道
它到底是哪一種。
有人用兩種說法對這 n 個動物所構成的食物鏈關係進行描述:
第一種說法是「1 x y」,表示 x 和 y 是同類。
第二種說法是「2 x y」,表示 x 吃 y 。
此人對 n 個動物,用上述兩種說法,一句接一句地說出 k 句話,這 k 句話有的是真
的,有的是假的。當一句話滿足下列三條之一時,這句話就是假話,否則就是真話。
• 當前的話與前面的某些真的話衝突,就是假話
• 當前的話中 x 或 y 比 n 大,就是假話
• 當前的話表示 x 吃 x,就是假話
你的任務是根據給定的 n 和 k 句話,輸出假話的總數。
輸入格式
從 eat.in 中輸入資料
第一行兩個整數,n,k,表示有 n 個動物,k 句話。
第二行開始每行一句話(按照題目要求,見樣例)
輸出格式
輸出到 eat.out 中
一行,乙個整數,表示假話的總數。
這道題描述的也是元素和元素之間的關係,但是這個關係稍顯複雜,因為這個題目裡面元素和元素之間的關係並不是單一的,有同類,被吃,和吃別人的關係,需要再路徑壓縮和並查集合並上作特殊處理,
參考了一下其他部落格的內容,作出的優化:
在這個題中相對關係就是食物鏈上的關係,因此帶權並查集中的權值就應該記錄兩個動物在食物鏈上的相對關係,a->b為0表示同類,為1表示a**,為2表示a被b吃。這個值不同於前面兩個題中的區間合、分數差,它是不可以直接累加的,要考慮三個問題:
1.路徑壓縮時,如何更新value
如果現在有a->b為1,b->c為1,怎麼求a->c?顯然a**,b吃c,那麼由題意c應該吃a,那麼a->c應該為2;
如果現在有a->b為2,b->c為2,怎麼求a->c?顯然b吃a,c**,那麼由題意a應該吃c,那麼a->c應該為1;
如果現在有a->b為0,b->c為1,怎麼求a->c?顯然a、b同類,b吃c,那麼由題意a應該吃c,那麼a->c應該為1;
找規律不難發現,a->c = (a->b + b->c) % 3,因此關係值的更新需要累加再模3。
2.區間合併時,如何更新value
由1不難發現,本題的value更新無非就是多了個取模操作,因此不難驗證區間合併的更新操作應該為:
relationwithparent[fx] = (-relationwithparent[x] + relation + relationwithparent[y]) % 3
#include
#include
#include
#include
#include
#include
#include
#include
#include
using
namespace std;
typedef
long
long ll;
#define pb push_back
#define mk make_pair
#define rep(a,b,c) for(int a=b;a#define repp(a,b,c) for(int a=b;a<=c;a++)
#define mem(a,b) memset(a,b,sizeof(a))
const
int inf=
0x3f3f3f3f
;const
int maxn=
5e4+10;
int root[maxn]
;int val[maxn]
;void
useiostream()
intfind
(int num)
return root[num];}
intmain()
else}}
cout<
}
溝通的一些心得體會
領導有新任務了,這個時候你需要了解了解任務的背景,整個任務最終需要達成的效果 目標是怎樣的?這個地方是最重要的,不同人可能有不同的理解,一定要保證大家的觀念一致,其次就是自己在任務中的角色是什麼?需要承擔什麼樣的義務?這個也是非常重要的,這個部分決定了你的工作量和對接物件,這裡你可能有兩種角色在裡面...
寫一些心得體會2007 1
自覺最近比較關注兩個方面 經濟 哲學,再加上本來就喜歡技術 這裡想說說自己汲取知識的範圍 經濟 哲學 技術以及其形成緣由,以供將來參考。經濟方面的關注主要來自 經濟觀察報 雖然其中文章多乾澀,基本都啃下來了,當然吸收程度只能說似懂非懂,不過能感覺這方面的素養比最初有所提高,這對自己是有鼓勵的。大概是...
擼羊毛的一些心得體會
羊毛黨 蘊含著強大勢能,危可斷獨角獸的角,用可建立增長神話。如同 道德經 道可道,非常道。羊有毛,非常毛。羊毛群體除了巨大的破壞力外,還具有強大的傳播勢能。了解利弊加以利用,從此以後爸爸再也不愁我的裂變活動沒有大量免費種子使用者傳播啦 但如果沒有系統性了解羊毛黨的破壞力,輕易嘗試引火燒身,重著被按在...