『8.24 模擬賽』ranwen的服務器
題目鏈接戳這裏n(*≧▽≦*)n
題目描述
眾所周知,ranwen建造出了強大的服務器網,規模十分龐大,有n個服務器,組成一個樹形結構,1號點是總站,但是有時候可能會因為主機被回收,機房斷電等事故造成服務器各種GG,現在有m個事件,可能為:1.查詢x到根路徑上第一個已經掛掉的服務器傳輸路徑 2.x到y路徑上的服務器傳輸路徑全掛了。(不存在則輸出0)
解題思路
上來先是樹剖,然而只過了樣例,爆0。。。
題面上提示了正解是並查集,但是並沒有想出來,聽完題解瞬間懂了。。。。
並查集,當然最方便的是合並操作,但是一個一個刪除,顯然不方便我們和並,那我們不妨倒著思考,將整棵樹一開始看做是壞的,後來慢慢變好,就是慢慢加入新的邊,那這樣我們就很方便維護了,將並查集的fa連向當前所在的聯通的的子樹的根,這樣我麽每次查詢的時候就是查詢每個節點的最高的父節點向上的邊的編號就好了。
但是這樣會出現一個問題,一條邊,可能在一開始就壞了,後來,又壞了一次,這時候我們加入邊的時候就不能在第二次壞掉的時候加入這條邊,那怎麽辦呢?
我們維護一個time數組,表示每個點向上的邊最早在哪裏壞掉,因為一開始壞掉,那麽災後後面不論你怎麽壞,不到一開始這條邊一直是壞的。這樣,我們正著做一遍並查集,每次合並刪除的邊上的節點,然後處理出我們的time數組,講節點深的點向上走,一直getfa到最上面。但如果跳的太遠。跳過了LCA怎麽辦呢?不用擔心這一點,因為依然我們能跳過
LCA,那麽說明這個點到LCA都已經被合並了,也就是有time的值了,而現在的time值不會對這些邊有影響。
所以,我們正著一遍並查集,處理壞掉的,然後暴力枚舉邊,得到最後好著的邊合並起來,再反著做一遍並查集,如果time的值等於當前的時間,那麽我們就合並壞的邊,最後走到一開始全是好的的情況,將答案記錄一下,倒著輸出就好了。
代碼
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=1000050; int n,m,cnt=0,tot=0; int fa[maxn],head[maxn<<1],ne[maxn<<1],to[maxn<<1],num[maxn<<1],dep[maxn],up[maxn],edge[maxn][2],tim[maxn],ans[maxn],fa_num[maxn]; struct nod{ int fl,x,y; }; nod q[maxn]; inline void read(int &x){ x=0; register char ch=getchar(); while(ch<'0'||ch>'9')ch=getchar(); while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar(); } inline void add(int f,int t,int nu){ ne[++cnt]=head[f],head[f]=cnt,to[cnt]=t,num[cnt]=nu; } inline void ini(){ for(register int i=0;i<maxn;i++)fa[i]=i; } inline int getfa(int x){ if(fa[x]==x)return x; else return fa[x]=getfa(fa[x]); } inline void dfs(int now,int f){ dep[now]=dep[f]+1; for(register int i=head[now];i;i=ne[i]){ if(to[i]!=f){ up[to[i]]=now; fa_num[to[i]]=num[i]; dfs(to[i],now); } } } inline void operate(int x,int y,int z){ x=getfa(x),y=getfa(y); while(x!=y){ if(dep[x]<dep[y])swap(x,y); if(!tim[x]){ tim[x]=z; fa[x]=fa[up[x]]; } x=getfa(x); } } inline void add_edge(int x,int y,int z){ while(x!=y){ if(dep[x]<dep[y])swap(x,y); if(tim[x]==z){ fa[x]=fa[up[x]]; } x=up[x]; } } int main(){ ini(); read(n),read(m); for(register int i=1,f,t;i<n;i++){ read(f),read(t); add(f,t,i),add(t,f,i); edge[i][0]=f,edge[i][1]=t; } dfs(1,0); for(register int i=1,j;i<=m;i++){ read(q[i].fl); if(q[i].fl==1)read(q[i].x); else { read(q[i].x),read(q[i].y); operate(q[i].x,q[i].y,i); } } ini(); for(register int i=1;i<n;i++){ int u=edge[i][0],v=edge[i][1]; if(dep[u]<dep[v])swap(u,v); if(!tim[u]){ fa[u]=v; } } for(register int i=m;i>=1;i--){ if(q[i].fl==1){ ans[++tot]=fa_num[getfa(q[i].x)]; } else { add_edge(q[i].x,q[i].y,i); } } for(register int i=tot;i>=1;i--){ printf("%d\n",ans[i]); } }
『8.24 模擬賽』ranwen的服務器