【並查集】猴子
阿新 • • 發佈:2019-02-13
題目描述
有N只猴子,第一隻尾巴掛在樹上,剩下的N-1只,要麼被其他的猴子抓住,要麼抓住了其他的猴子,要麼兩者均有。當然一隻猴子最多抓兩隻另外的猴子。現在給出這N只猴子抓與被抓的資訊,並且在某個時刻可能某隻猴子會放掉它其中一隻手的猴子,導致某些猴子落地。求每隻猴子落地的時間。
輸入輸出格式
輸入格式:第一行兩個數N、M,表示有N只猴子,並且總時間為M-1。接下來N行,描述了每隻猴子的資訊,每行兩個數,分別表示這隻猴子左手和右手抓的猴子的編號,如果是-1,表示該猴子那隻手沒抓其他猴子。再接下來M行,按時間順序給出了一些猴子放手的資訊,第1+N+i行表示第i-1時刻某隻猴子的放手資訊,資訊以兩個數給出,前者表示放手的猴子編號,後者表示其放的是哪隻手,1左2右。
【資料規模】
30%的資料,N≤1000,M≤1000;
100%的資料,1≤N≤200000,1≤M≤400000。
輸出格式:共輸出N行,第i行表示第i只猴子掉落的時刻,若第i只猴子島M-1時刻以後還沒掉落,就輸出-1。
輸入輸出樣例
輸入樣例#1:3 2 -1 3 3 -1 1 2 1 2 3 1輸出樣例#1:
-1 1 1
分析:這題其實就是個無向的連通圖,求刪除一條邊後圖的連通性。 正向去刪邊判斷確實有點麻煩,我們不妨把操作存起來,從最後一個操作開始逆向加邊, 當兩個點第一次連通時,就是那兩隻猴子掉下來的時候。 這裡用到帶權並查集,也是把每個點的權值都賦為他祖宗的權值,而權值就是掉下來的時間。
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <queue> #include <set> #include <map> #include <vector> #include <list> #define Open(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout); #define Close fclose(stdin);fclose(stdout); #define rep(i,m,n) for(int i=m;i<=n;i++) #define dop(i,m,n) for(int i=m;i>=n;i--) #define lowbit(x) (x&(-x)) #define ll long long #define INF 2147483647 using namespace std; inline int read(){ //快讀 int s=0,w=1; char ch=getchar(); while(ch<='0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return s*w; } const int maxn=200010; const int maxm=400010; int n,m; int s[2][maxn],f[maxn],ans[maxn]; //s存左右兒子,s[0]是左,s[1]是右,f:並查集,ans:答案 bool v[2][maxn]; //v存要斷的邊,和鄰接矩陣原理一樣 struct fame{int f,s;}e[maxm]; int find(int x){ //帶權並查集 if(f[x]==x) return x; int temp=find(f[x]); ans[x]=min(ans[x],ans[f[x]]); return f[x]=temp; } void get_fa(int x,int y,int z){ //x,y是要連的兩隻猴子,z是時間 x=find(x),y=find(y); if(x!=y){ if(x==1) f[y]=x,ans[y]=z; else f[x]=y,ans[x]=z; } } int main(){ n=read();m=read(); for(int i=1;i<=n;i++){ s[0][i]=read();s[1][i]=read(); f[i]=i;ans[i]=maxm; } for(int i=1;i<=m;i++){ scanf("%d%d",&e[i].f,&e[i].s); v[e[i].s-1][e[i].f]=1; } for(int i=1;i<=n;i++){ if(!v[0][i]&&s[0][i]!=-1) get_fa(i,s[0][i],m); //把有的邊連上 if(!v[1][i]&&s[1][i]!=-1) get_fa(i,s[1][i],m); } for(int i=1;i<=n;i++) ans[i]=maxm; //初始值賦為一個極大數 for(int i=m;i>=1;i--) if(s[e[i].s-1][e[i].f]!=-1) get_fa(e[i].f,s[e[i].s-1][e[i].f],i-1); //逆向連邊,更新ans for(int i=1;i<=n;i++) find(i),printf("%d\n",ans[i]==maxm?-1:ans[i]); return 0; }