【JZOJ3973】【NOI2015模擬1.10】【NOI2013湖南省隊集訓】黑白樹(wbtree)(並查集)
Problem
給定一棵樹,邊的顏色為黑或白,初始時全部為白色。維護兩個操作:
1. 查詢u到根路徑上的第一條黑色邊的標號。
2. 將u到v路徑上的所有邊的顏色設為黑色。
Hint
對於30%的資料:
對於100%的資料:
Solution
這種題讓人一看就想到鏈剖,但是看看資料範圍:,便知道鏈剖那的超高複雜度肯定卡不過去。
於是想到的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向它在原圖中的父親節點連邊。當然,為了壓縮路徑,我們在並查集中直接向該父親的根連邊。這樣即可在
隨後,我們將所有的邊的資訊(編號及變黑的時間)和所有詢問放在一個數組裡,以時間為關鍵字從大到小排序。這樣,我們就順序掃一遍,相當於按原時間倒過來做一遍。
此時,我們須建一棵只含白邊的樹。每掃到有一條邊變黑時,由於我們是倒過來做的,所以其實相當於將染色撤銷,即將此邊變回白色,我們在並查集中就從它向它原圖中的父親節點的根連邊;每掃到一個詢問點u時,我們就直接跳到u的根,而此時的根連向其父親節點的邊則定為u到原根路徑上的第一條黑色邊,那麼輸出此邊的編號。
最後,由於未知原因,我這個程式跑得賊慢,T了好幾遍,所以一定要打輸入輸出優化。
時間複雜度:
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);
}
}