【牛客 - 368D】动态连通块(并查集+bitset优化)
題干:
小T有n個(gè)點(diǎn),每個(gè)點(diǎn)可能是黑色的,可能是白色的。
小T對這張圖的定義了白連通塊和黑連通塊:
白連通塊:圖中一個(gè)點(diǎn)集V,若滿足所有點(diǎn)都是白點(diǎn),并且V中任意兩點(diǎn)都可以只經(jīng)過V中的點(diǎn)互相到達(dá),則稱V中的點(diǎn)構(gòu)成了一個(gè)白連通塊。
黑連通塊:類似白連通塊的定義。
小T對這n個(gè)點(diǎn)m次操作。
1、在兩個(gè)點(diǎn)之間連一條邊。
2、詢問白(黑)連通塊個(gè)數(shù)。
3、給出x,y兩個(gè)點(diǎn),保證同色(為了方便描述,x,y都是白點(diǎn),黑色同理)。詢問存在多少個(gè)黑點(diǎn),將它改變顏色后,x,y所在的白連通塊會(huì)合并為一個(gè)。如果x,y已經(jīng)在一個(gè)白連通塊內(nèi)了,輸出-1。(注意:這里不會(huì)對點(diǎn)的顏色改變,只統(tǒng)計(jì)個(gè)數(shù))
?
?
輸入描述:
第一行兩個(gè)整數(shù)n,m,分別表示點(diǎn)的個(gè)數(shù)和操作的個(gè)數(shù)。 第二行n個(gè)整數(shù),第i個(gè)整數(shù)描述第i個(gè)點(diǎn)的顏色,0表示白色,1表示黑色。 接下來m行,每行包含2個(gè)或者3個(gè)整數(shù),描述三種操作。 操作1:1,x,y,表示在x,y之間加入一條邊。 操作2:2,x,若x=0,詢問白連通塊的個(gè)數(shù),否則詢問黑連通塊的個(gè)數(shù)。 操作3:3,x,y,表示第三種操作。 n,m≤50000,x,y≤nn,m≤50000,x,y≤n輸出描述:
對于詢問操作,輸出一個(gè)整數(shù)。示例1
輸入
復(fù)制
6 7 0 1 0 0 0 1 1 3 2 1 2 4 3 3 4 1 1 3 2 0 3 1 4 3 1 3輸出
復(fù)制
1 3 1 -1說明
第一次詢問:2號(hào)點(diǎn)變成白色后,3,4所在的白連通塊合并為一個(gè)。 第二次詢問:白連通塊的個(gè)數(shù)為3,分別是{1,2},{3},{4}。 第三次詢問:2號(hào)點(diǎn)變成白色后,1,4所在的白連通塊合并為一個(gè)。 第四次詢問:1,3已經(jīng)是同一個(gè)白連通塊了,輸出-1。解題報(bào)告:
并查集+bitset優(yōu)化。
操作二可以在加邊的過程中求出。即加入一條端點(diǎn)同色的邊,并查集判斷是否可以消去一個(gè)白(黑)連通塊。
操作三求的是x,y所在的兩個(gè)白連通塊都連出的黑點(diǎn)(假設(shè)x,y是白色,黑色的情況是一樣的)。
于是可以bitset維護(hù)每個(gè)白聯(lián)通塊連出的黑點(diǎn),黑連通塊連出的白點(diǎn)。每加入一條端點(diǎn)異色的邊,在兩個(gè)白、黑連通塊的bitset中將一個(gè)位置設(shè)為1;加入一條端點(diǎn)同色的邊,就是合并白(黑)連通塊的過程,同時(shí)合并兩個(gè)白(黑)連通塊的bitset。
復(fù)雜度,W=32或64
AC代碼:
#include<cstdio> #include<iostream> #include<algorithm> #include<queue> #include<map> #include<vector> #include<set> #include<string> #include<cmath> #include<bitset> #include<cstring> #define ll long long #define pb push_back #define pm make_pair #define fi first #define se second using namespace std; const int MAX = 6e4 + 5; int col[MAX]; int f[MAX]; int totbai,tothei,bai,hei; bitset<MAX> bs[MAX]; int getf(int v) {return v == f[v] ? v : f[v] = getf(f[v]); } void add(int u,int v) {int t1 = getf(u);int t2 = getf(v);if(col[u] == col[v]) {if(t1 != t2) {if(col[u] == 0) bai--;else hei--; }f[t2] = t1;bs[t1] |= bs[t2];//這兩句也可以放在if里 }else {bs[t1].set(v);//bs[t1][v]=1;bs[t2].set(u);//bs[t2][u]=1;} } int main() {int n,m;cin>>n>>m;for(int i = 1; i<=n; i++) {scanf("%d",col+i);f[i] = i;}for(int i = 1; i<=n; i++) {if(col[i] == 1) hei++,tothei++;else bai++,totbai++;}for(int op,x,y,i = 1; i<=m; i++) {scanf("%d",&op);if(op == 1) {scanf("%d%d",&x,&y);add(x,y);}else if(op == 2) {scanf("%d",&x);printf("%d\n",x == 1 ? hei : bai);}else {scanf("%d%d",&x,&y);int t1 = getf(x);int t2 = getf(y);if(t1 == t2) puts("-1");else {printf("%d\n",(bs[t1]&bs[t2]).count());}}}return 0 ;}其實(shí)merge函數(shù)這樣寫比較清晰:
void merge(int u,int v) {int t1 = getf(u),t2 = getf(v);if(t1 == t2) return;if(col[t1] == col[t2]) { if(col[t1] == 1) hei--;else bai--;f[t2]=t1;bs[t1] |= bs[t2];}else bs[t1][v]=bs[t2][u]=1; }?
簡化版:
const int N=5e4+5; int n,m,col[N],fa[N],num[2];bitset<N>S[N]; int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);} int main(){n=gi();m=gi();for(int i=1;i<=n;++i)++num[col[i]=gi()],fa[i]=i;while(m--){int op=gi(),x=gi(),y;if(op&1)y=gi();if(op==1)if(col[x]^col[y])S[find(x)][y]=1,S[find(y)][x]=1;else{x=find(x),y=find(y);if(x^y)fa[y]=x,S[x]|=S[y],--num[col[x]];}if(op==2)printf("%d\n",x?num[1]:num[0]);if(op==3)printf("%d\n",find(x)^find(y)?(int)(S[find(x)]&S[find(y)]).count():-1);}return 0; }總結(jié):
? 這個(gè)tothei和totbai,計(jì)算過程中始終沒有用到,但是作為一個(gè)流程性的東西,萬一給你改變一下題目,說不定就用到了,所以提前維護(hù)出來為好。?
總結(jié)
以上是生活随笔為你收集整理的【牛客 - 368D】动态连通块(并查集+bitset优化)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【CodeForces - 520B】T
- 下一篇: 【ZOJ - 4029】Now Load