P1196 [NOI2002]銀河英雄傳說(帶權並查集)node
題意:ios
有n艘艦依次排序,每次將i及其身後的艦艇合併至j及其全部艦艇以後,每次詢問i到j艦艇之間的距離,若是不在一列輸出-1數組
思路:ide
單純的合併與查詢是否在一列操做比較簡單,難的在於查詢距離spa
首先咱們須要三個數組fa[i],sum[i],dis[i]分別爲i的父親,i列全部的艦艇數,與其到其父親的距離3d
可能有人會想i到其父親的距離不都是1嘛,其實在路徑壓縮過程當中,父親會與實際的狀況不符,雖然直接相連可是可能距離並不爲1code
如今考慮合併(i,j)操做,每次合併操做只要對第一艘艦艇進行修改就行了,分別修改blog
dis數組的修改直接等於sum[j],以後將sum[j]+=sum[i],並將sum[i]=0排序
在每次查詢時都會進行路徑壓縮,所以dis[k](k爲排在i以後的艦艇)雖然在合併時沒有修改,可是會在路徑壓縮(查詢父親)時修改爲到該列第一艘艦艇的距離ci
以後在利用前綴後的思想,(dis[i]-dis[j])-1即爲連個艦艇之間的艦艇數了
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> using namespace std; const int maxn=3e4+10; int fa[maxn],sum[maxn],dis[maxn]; int find(int x) { if(fa[x]==x) return x; int f1=fa[x],f2=find(fa[x]); dis[x]+=dis[f1]; fa[x]=f2; return f2; } void uni(int x,int y) { int f1=find(x),f2=find(y); fa[f2]=f1; dis[f2]=sum[f1]; sum[f1]+=sum[f2]; sum[f2]=0; } int main() { int t,i,j; char op; scanf("%d",&t); for(int i=1;i<=maxn;i++){ fa[i]=i; sum[i]=1; } while(t--){ cin>>op>>i>>j; if(op=='M') uni(j,i); else{ if(find(i)!=find(j)) cout<<-1<<endl; else{ cout<<abs(dis[j]-dis[i])-1<<endl; } } } return 0; }
P2024 [NOI2001]食物鏈(種類並查集)
題意:
如今有三種生物,ABC,A吃B,B吃C,C吃A,如今依次告訴你一些關係,請說出這些關係中假話的個數(假話的定義爲與以前的話矛盾或不符合事實例如A吃A)
思路:
對於一對關係,比較難處理的是雖然你知道X吃Y,可是你不知道X跟Y究竟屬於什麼物種
那麼咱們能夠創建3*N大小的並查集,分爲A,B,C三個部分,表明着A中的生物吃B中的生物等等類推
對於一個關係好比X跟Y一類,咱們在三個集合中分別將兩者相連
對於關係X吃Y,咱們就將A中的X與B中的Y相連,還有兩個集合中操做相似
對於每一個關係,咱們就能夠經過判斷在一個集合內是否相連,或者在另外一個集合內相連來判斷正誤了
#include<iostream> #include<algorithm> #include<cstring> #include<cstdio> using namespace std; const int maxn=2e5+10; int fa[maxn]; int find(int x){return fa[x]==x?x:(fa[x]=find(fa[x]));} int main() { int n,k,ans=0,op,x,y; scanf("%d%d",&n,&k); for(int i=1;i<=3*n;i++) fa[i]=i; for(int i=1;i<=k;i++){ scanf("%d%d%d",&op,&x,&y); if(x>n||y>n){ ans++; continue; } if(op==1){ if(find(x)==find(y+n)||find(y)==find(x+n)) ans++; else{ fa[find(x)]=find(y); fa[find(x+n)]=find(y+n); fa[find(x+2*n)]=find(y+2*n); } } else{ if(x==y){ans++;continue;} if(find(x)==find(y)||find(x)==find(y+2*n)) ans++; else{ fa[find(x)]=find(y+n); fa[find(x+n)]=find(y+2*n); fa[find(x+2*n)]=find(y); } } } cout<<ans<<endl; return 0; }
P1197 [JSOI2008]星球大戰(逆向思惟,並查集)
題意:
給你一個無向圖,每次從圖中刪去一個點,詢問每次刪點事後圖中連通塊的數量
思路:
本題能夠離線,所以咱們採用離線的逆向作法
怎麼個逆向呢?咱們假設一開始只有全部刪點操做以後的點,並算出連通塊個數
以後每次向圖中加入被刪除的點,並統計連通塊個數
若是從新對全部點跑一遍的話時間複雜度上必定會炸,對於新加入的點,咱們先對當前連通塊個數加1,若是遍歷該點鏈接的全部點,若是可以合併,那麼就將連通塊個數減1
最後把答案倒序輸出就行了
#include<cstdio> #include<iostream> #include<algorithm> #include<vector> #include<stack> #include<cstring> using namespace std; const int maxn=4e5+1000; int flag[maxn],fa[maxn]; vector<int>a[maxn]; stack<int> s,q; int find(int x){return fa[x]==x?x:(fa[x]=find(fa[x]));} int main() { int n,m,k,u,v; memset(flag,1,sizeof(flag)); scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ scanf("%d%d",&u,&v); a[u].push_back(v); a[v].push_back(u); } scanf("%d",&k); for(int i=1;i<=k;i++){ scanf("%d",&u); s.push(u); flag[u]=0; } int cnt=n-k; for(int i=0;i<n;i++) fa[i]=i; for(int i=0;i<n;i++){ if(!flag[i]) continue; else{ for(int j=0;j<a[i].size();j++){ if(flag[a[i][j]]){ int f1=find(a[i][j]),f2=find(i); if(f1!=f2) fa[f1]=find(fa[f2]),cnt--; } } } } q.push(cnt); for(int i=1;i<=k;i++){ cnt++; int x=s.top(); s.pop(); for(int j=0;j<a[x].size();j++){ if(flag[a[x][j]]){ int f1=find(a[x][j]),f2=find(x); if(f1!=f2) fa[f1]=find(fa[f2]),cnt--; } } flag[x]=1; q.push(cnt); } while(!q.empty()){ cout<<q.top()<<endl; q.pop(); } return 0; }
P1111 修復公路(並查集)
題意:
給你n個點,m條無向邊,每條邊建好都有一個時間,問何時各個點能互相可達
思路:
將每條邊按時間排序,每次加入一條邊,看邊鏈接的兩點是否在一個連通塊內,不在的話合併連通塊,看是否總連通塊個數爲1便可
#include<iostream> #include<algorithm> #include<cstring> #include<cstdio> using namespace std; const int maxn1=1e5+10; const int maxn2=1e3+10; int fa[maxn2],siz[maxn2],n,m; struct node{ int u,v,t; }edge[maxn1]; int cmp(node a,node b){return a.t<b.t;} int find(int x){return fa[x]==x?x:(fa[x]=find(fa[x]));} int main() { scanf("%d%d",&n,&m); for(int i=0;i<m;i++) scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].t); sort(edge,edge+m,cmp); for(int i=1;i<=n;i++) fa[i]=i; memset(siz,0,sizeof(siz)); int cnt=n; for(int i=0;i<m;i++){ int u=edge[i].u,v=edge[i].v,t=edge[i].t; int f1=find(u),f2=find(v); if(f1!=f2) fa[f1]=f2,cnt--; if(cnt==1){ cout<<t<<endl; return 0; } } cout<<-1<<endl; return 0; }