容易發現,當水位線確定了,圖會被分成若干個連通塊,連通塊裡的點可以互相到達,故連通塊內點的答案為其中的點到\(1\)的距離的最小值,這個可以先\(dijkstra\)預處理求出(\(spfa\)死了)。
那考慮怎麼維護連通塊。可持久化並查集?感覺不好搞,有一種神奇的演算法——\(kruscal\)重構樹。
其實就是\(kruscal\)求最小生成樹時,每一次合併兩個點都建乙個新點,新點的點權被賦值為兩點間邊的邊權,並使其成為兩個點新的父親。這顆樹有個優秀的性質——這是乙個二叉堆。
先按海拔從大到小把邊排序,再建出\(kruscal\)重構樹,把新點的點權賦值為邊的海拔。這樣,從根到葉節點,點權,即海拔是單調遞增的,並且每乙個點的點權都小於其子樹內的點權。
有了這個,我們可以先dfs求出子樹內葉子節點到\(1\)距離的最小值,之後從給定的\(u\)樹上倍增跳至\(u\)的祖先\(v\),滿足\(v\)的海拔大於給定的\(p\),點\(v\)子樹內的所有葉子節點即是滿足條件的連通塊內的點。
#includeusing namespace std;
int n,m,q,k,s,cnt,tot,last;
int head[400005],w[800005],to[800005],next[800005];
int h[400005],fa[400005],vis[400005],dis[400005],anc[400005][25];
struct edge
}e[400005];
struct node
};inline int read()
while(ch>='0'&&ch<='9')
return x*f;
}inline void add(int u,int v,int p)
inline int find(int x)
void dfs(int u,int f)
return;
}int query(int u,int x)
return dis[u];
}void dijkstra());dis[1]=0;
while(!q.empty()));}}
}return;
}void kruscal()
if(tot==n+n-1)
break;
}dfs(tot,0);
return;
}void work();
add(e[i].u,e[i].v,e[i].w);
add(e[i].v,e[i].u,e[i].w);
}dijkstra();kruscal();
last=0;q=read();k=read();s=read();
for(register int i=1;i<=q;++i)
return;
}int main()
NOI 2018 歸程 Kruskal重構樹
題目大意 太長了,略 kruskal重構樹,很神奇的乙個演算法吧 如果兩個並查集被某種條件合併,那麼這個條件作為乙個新的節點連線兩個並查集 那麼在接下來的提問中,如果某個點合法,它的所有子節點也都合法,即子節點的限制少於父節點 include include include include defi...
NOI2018 歸程(kruskal重構樹)
思路 每天的最短距離相當於每天走高於海拔高於水位線的路所能到達的所有點中到1的最短距離。現在要想辦法能夠直接查詢這個最短距離,如果能根據海拔構造一種樹狀結構,那麼每次能開車到達的點對應一顆子樹,那麼只需要倍增出這顆子樹的根節點就可以了。kruskal重構樹 對海拔從大到小排序,然後跑kruskal,...
NOI2018 歸程 kruskal重構樹
lg傳送門 kruskal重構樹模板題。另一篇文章裡有關於kruskal重構樹更詳細的介紹和更板子的題目。題意懶得說了,這題的關鍵在於快速找出從查詢的點出發能到達的點 即經過海拔高於水位線的邊能到達的點 中距離 1 號點最近的距離。看上去可以kruskal,假設我們把邊實現按海拔從大到小排序,考慮我...