1. 程式人生 > >【JZOJ3973】【NOI2015模擬1.10】【NOI2013湖南省隊集訓】黑白樹(wbtree)(並查集)

【JZOJ3973】【NOI2015模擬1.10】【NOI2013湖南省隊集訓】黑白樹(wbtree)(並查集)

Problem

  給定一棵樹,邊的顏色為黑或白,初始時全部為白色。維護兩個操作:
1. 查詢u到根路徑上的第一條黑色邊的標號。
2. 將u到v路徑上的所有邊的顏色設為黑色。

Hint

  對於30%的資料:n,m103
  對於100%的資料:n,m106

Solution

  這種題讓人一看就想到鏈剖,但是看看資料範圍:106,便知道鏈剖那O(n(log2n)2)的超高複雜度肯定卡不過去。
  於是想到O(nlog2n)的LCT,但是LCT常數巨大,多數情況下比鏈剖還慢,所以更不可能。
  然而我比賽時又想不到好的方法,只好規規矩矩地打了個鏈剖,又頗感某個地方可能打錯了,所以估了0分,結果真的爆0;然而,zhj竟很快憑藉一個暴力水過了。所以我不得不祝出題人身體健康。
  雖然我至今仍未發現我的鏈剖哪裡打錯了,但這不是重點——反正鏈剖也會T。正解是並查集。
  首先,我們可在讀入的時候按照它的讀入順序建一棵只含黑邊的樹,這樣即可求出每一條邊被染黑的時間(設至始至終為白色的邊變黑的時間為m+1)。我們可以暴力連邊,畢竟一條邊最多隻會被染一次。
  但是我們可不能傻乎乎地一條一條邊往上跳。我一開始打了個倍增lca,但是T了。於是我發現我們可以用一種較為神奇的方法連邊:如要將x到y的路徑變黑,設deep[x]>=deep[y],則將x跳到根,若此時deep[x]依然>=deep[y]且x≠y,則表明x不為它們的lca或它們lca的祖先,那麼我們便可將此時的x向它在原圖中的父親節點連邊。當然,為了壓縮路徑,我們在並查集中直接向該父親的根連邊。這樣即可在

O(nα(n))內的時間完成連邊。
  隨後,我們將所有的邊的資訊(編號及變黑的時間)和所有詢問放在一個數組裡,以時間為關鍵字從大到小排序。這樣,我們就順序掃一遍,相當於按原時間倒過來做一遍。
  此時,我們須建一棵只含白邊的樹。每掃到有一條邊變黑時,由於我們是倒過來做的,所以其實相當於將染色撤銷,即將此邊變回白色,我們在並查集中就從它向它原圖中的父親節點的根連邊;每掃到一個詢問點u時,我們就直接跳到u的根,而此時的根連向其父親節點的邊則定為u到原根路徑上的第一條黑色邊,那麼輸出此邊的編號。
  最後,由於未知原因,我這個程式跑得賊慢,T了好幾遍,所以一定要打輸入輸出優化。
  時間複雜度:
O(nα(n))

Code

#include <cstdio>
#include <cctype>
#include <algorithm>
#include <cstring>
using namespace std;
#define N 1000010
#define M 2*N
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
int i,n,m,u,v,tot,tov[M],next[M],last[N],d[N],fat[N],deep[N],num[N],f,fa[N],side[N],qs,ws,x
,t,ans[N]; bool p; struct operation { int x,u,v; }a[N]; struct Q { int x,t; bool p; }q[M]; struct mes { int x; bool p; mes(){} mes(int _x,bool _p){x=_x;p=_p;} }D[M]; void read(int &x) { char c=getchar(); x=0; for(;!isdigit(c);c=getchar()); for(;isdigit(c);x=(x<<3)+(x<<1)+c-'0',c=getchar()); } inline void insert(int x,int y) { tov[++tot]=y; next[tot]=last[x]; last[x]=tot; } void print(int x) { if(x>9)print(x/10); putchar(x%10+'0'); } void bfs() { int i,head=0,tail=d[1]=1,x,y; while(head<tail) { x=d[++head]; for(i=last[x];i;i=next[i]) { y=tov[i]; if(y!=fat[x]) { fat[y]=x; deep[y]=deep[x]+1; num[y]=(i+1)/2; d[++tail]=y; } } } } int gef(int x) { int i,top=0; while(x)d[++top]=x,x=fa[x]; fo(i,1,top-1)fa[d[i]]=d[top]; return d[i]; } void blacken(int x,int y) { while(x!=y) { if(deep[x]<deep[y])swap(x,y); if(deep[x=gef(x)]>=deep[y]&&x!=y)side[x-1]=i,x=fa[x]=gef(fat[x]); } } inline void ins(int t,int x,bool p) { D[++tot]=mes(x,p); next[tot]=last[t]; last[t]=tot; } int main() { read(n);read(m); fo(i,1,n-1) { read(u);read(v); insert(u,v); insert(v,u); } bfs(); tot=0; memset(next,0,sizeof next); memset(last,0,sizeof last); fo(i,1,m) { read(a[i].x);read(a[i].u); if(a[i].x==1) { ws++; ins(i,a[i].u,1); continue; } read(a[i].v); blacken(a[i].u,a[i].v); } fo(i,1,n-1)ins(side[i]?side[i]:m+1,i+1,0); fd(t,m+1,1) { i=last[t]; while(i) { q[++qs].x=D[i].x; q[qs].t=t; q[qs].p=D[i].p; i=next[i]; } } memset(fa,0,sizeof fa); fo(i,1,qs) { if(q[i].p) { f=gef(q[i].x); ans[ws-x++]=num[f]; continue; } fa[q[i].x]=gef(fat[q[i].x]); } fo(i,1,x) { print(ans[i]); putchar(10); } }