CF1166F - Vicky's Delivery Service(並查集,啟發式合併)
阿新 • • 發佈:2021-11-08
給出節點數為\(n\),邊數為\(m\)的圖。
保證每個點對都是互連的。
定義彩虹路:這條路經過\(k\)個節點,對於\(x(x\%2=0)\)的節點,左右兩條邊顏色相同。
現在有\(q\)次操作。
第一種操作是新增一條邊。
第二種操作是回答是否能經過彩虹邊從\(a\)節點到達\(b\)節點。
做法:
能相互到達的點用並查集連起來。
具體做法就是:
當\(a-b-c\)的邊的顏色相同時,我們把\(a\)和\(c\)節點用合併,代表\(a\)和\(c\)互連。
對於節點\(a\),每個顏色的邊只需要儲存一條,這樣可以快速合併節點。
這裡可以列舉b的邊集,按序合併即可。
但是還有一種情況,\(x\)
所以如果兩個節點不在一個集合內,就需要看其中一個點能否通過另一個集合到達。
對每個集合維護一個set,然後列舉每條邊,把這條邊兩端的節點塞進另一個節點的集合的set。
加邊的時候,對邊兩邊的節點維護並查集。
合併集合的時候,對集合對應的set,啟發式合併即可。
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+10; int n,m,c,q; map<int,int> g[maxn]; set<int> st[maxn]; int father[maxn]; int findfather (int x) { int a=x; while (x!=father[x]) x=father[x]; while (a!=father[a]) { int z=a; a=father[a]; father[z]=x; } return x; } void un (int x,int y) { x=findfather(x); y=findfather(y); if (x==y) return; if (st[x].size()<st[y].size()) { father[x]=y; for (auto it:st[x]) st[y].insert(it); } else { father[y]=x; for (auto it:st[y]) st[x].insert(it); } } int main () { scanf("%d%d%d%d",&n,&m,&c,&q); for (int i=1;i<=n;i++) father[i]=i; while (m--) { int x,y,z; scanf("%d%d%d",&x,&y,&z); st[findfather(y)].insert(x); st[findfather(x)].insert(y); if (g[x].count(z)) { un(g[x][z],y); } else { g[x][z]=y; } if (g[y].count(z)) { un(g[y][z],x); } else { g[y][z]=x; } } while (q--) { char op; getchar(); scanf("%c",&op); if (op=='+') { int x,y,z; scanf("%d%d%d",&x,&y,&z); st[findfather(y)].insert(x); st[findfather(x)].insert(y); if (g[x].count(z)) { un(g[x][z],y); } else { g[x][z]=y; } if (g[y].count(z)) { un(g[y][z],x); } else { g[y][z]=x; } } else { int x,y; scanf("%d%d",&x,&y); if (findfather(x)==findfather(y)) { printf("Yes\n"); } else if (st[findfather(x)].count(y)) { printf("Yes\n"); } // else if (st[findfather(y)].count(x)) { // printf("Yes\n"); // } else { printf("No\n"); } } } }