演算法

單點源最短距離

1到4的最短距離?

Dijkstra’s Algorithm

前提:邊的權重不能有負的

找可以確定的最短距離

時間複雜度O(E \log E)

實作

紀錄有沒有走過

priority_queue

#include<bits/stdc++.h>
using namespace std;
#define pii pair<int,int>
#define ff first
#define ss second
#deifne N 200010
vector<pii> adj[N];
bitset<N> vis;
int dis[N];
void Dijkstra(int v){
    priority_queue<pii> pq;
    for(auto u:adj[v]) pq.push({-u.ss,u.ff});
    vis[v]=1;
    while(!pq.empty()){
        auto v=pq.top();pq.pop();
        if(vis[v.ss]) continue;
        vis[v.ss]=1,dis[v.ss]=-v.ff;
        for(auto u:adj[v]) pq.push({v.ff-u.ss,u.ff});
    }
}

最小生成樹

使一個圖連通的最小權重

Prim’s algorithm

跟Dijkstra幾乎一樣

時間複雜度O(E \log E)

Kruskal’s algorithm

觀察一下

只要取不同連通塊的最小的邊就好了

時間複雜度O((V+E) \log E)

並查集

有兩種操作

1.查詢x在哪個集合

2.合併a, b集合

可以讓每個集合可以有一個代表元素

這樣就可以判連通塊了

並查集

實作

 

int root[x];
//若root[x]<0
//則x為該集合的代表元素
int find(int x){
    if(root[x]<0) return x;
    return find(root[x]);
}
void Union(int a,int b){
    a=find(a),b=find(b);
    if(a==b) return;
    root[b]=a;
}

小的合到大的

啟發式合併

路徑壓縮

int find(int x){
    if(root[x]<0) return x;
    return root[x]=find(root[x]);
}
if(size[a]<size[b]) swap(a,b);
size[a]+=size[b];

如果加上面兩個優化並查集的複雜度小到可以當常數

只加一個會降成log

題目

單點源最短路

tcirc d096

最小生成樹

tcirc d098

tioj 2164

並查集

tcirc d101

tcirc d097

LCA

時間戳記

啟發式合併

LCA最低共同祖先

(u,v)深度最深的共同祖先即(u,v)的lca

LCA最低共同祖先

#include<bits/stdc++.h>
using namespace std;
#define N 200010
int n,dep[N],fa[20][N];
vector<int>c[N];
void dfs(int v,int f){
	for(int u:c[v]) if(u!=f){
		dep[u]=dep[v]+1;
		fa[0][u]=v;
	   	dfs(u,v);
	}
}
void init(){
	dfs(1,-1);
	for(int i=1;i<20;i++) for(int j=1;j<=n;j++)
	   	fa[i][j]=fa[i-1][fa[i-1][j]];
}
int lca(int u,int v){
	if(dep[u]<dep[v]) swap(u,v);
	int d=dep[u]-dep[v];
	for(int i=19;i>=0;i--)
		if(d&(1<<i)) u=fa[i][u];
	if(u==v) return v;
	for(int i=19;i>=0;i--)
		if(fa[i][u]!=fa[i][v]) u=fa[i][u],v=fa[i][v];
	return fa[0][u];
}

LCA最低共同祖先

(u,v) 的距離=dep[u]+dep[v]-dep[lca(u,v)]*2

時間戳記

紀錄出入的時間

in:1 2 3       4 5   6

out:           3 2     5   6 4 1

時間戳記

判斷u是否為v的祖先

in[v]<in[u]&&out[v]>out[u]

Text

時間戳記

把回頭編號加上去

1 2 3 3 2 1 4 5 5 4 6 6 4 1

(u,v)經過的路徑

in[u]<in[v]

if out[u]>in[v] 經過的路徑為(in[u],in[v])

else (out[u],in[v])

時間戳記

啟發式合併

deck

By chenyanjun

deck

  • 88