線段樹優化建邊

2021-09-28 11:31:59 字數 3852 閱讀 8387

完整**

總結例題:車站分級

這道題乍一看就能想到等級低的向等級高的連邊,即兩停靠站之間的站點向兩車站連邊,最後跑一遍拓撲排序找出最大值即為答案。

以題目樣例為例,假設火車經過站點如下,其中藍色代表停靠,紅色代表不停靠:

也就是說,2的等級一定比1、3、5和6低,4的等級一定比1、3、5和6低,建邊如下:

拓撲排序出來就是兩層,答案為2.

但是,當圖中的點越來越多時,時間複雜度和空間複雜度都會**,這時我們就要引入一種優化方法——建虛點!將2和4連向我們新開的虛點,再將虛點連向1、3、5和6。可以證明,這樣對原圖是沒有影響的。

但是這樣的話虛點對答案也有貢獻,所以經過虛點時要特判一下。

可是,如果這樣都還是不滿足時空複雜度的話,還需要更優秀的建圖方法。看到兩個車站間所有連續的節點的等級都比端點車站的等級小,於是想到可以用資料結構優化建圖。下面我們就來介紹線段樹優化建圖。

顯而易見,這裡用到了線段樹的思想。

當向乙個區間的每乙個節點都連邊時,為了減少連邊個數,我們先建立一顆線段樹,然後向線段樹中代表該區間的節點連邊,這樣可以使一次建邊的個數變為log⁡n

\log n

logn

,既優化了時間,也優化了空間。

普通的線段樹都會建,但是這道題因為是一顆圖,所以要考慮連邊。因為最後要拓撲排序,所以子節點向父親連邊。

需要注意的是,線段樹的節點編號要從n+1

n+1n+

1開始(顯然),但是若遇到了葉子結點,就將該點的編號設為葉子節點的編號,以n=10

n=10

n=10

為例,如圖所示(其中黑色數字為線段樹節點代表的區間,紅色數字為線段樹節點的新編號):

**如下,其實與普通的線段樹差不多,注意ndn

umndnum

ndnum從n+1

n+1n+

1開始:

void

build

(int

&k,int l,

int r)

k=++ndnum;

//傳新編號

int mid=

(l+r)

>>1;

build

(lid,l,mid)

;build

(rid,mid+

1,r)

;//下傳左右兒子

add(lid,k)

;add

(rid,k)

;//向父節點連邊

}

1 8

建邊如下(紅色為新邊):

因為涉及到線段樹內的建邊,所以建邊時還要向區間修改和區間查詢操作一樣,從線段樹的根節點開始走,一直走到要建邊的區間內的節點。

**:

void

addtr

(int k,

int l,

int r,

int l,

int r)

int mid=

(l+r)

>>1;

if(l<=mid)

addtr

(lid,l,mid,l,r);if

(r>mid)

addtr

(rid,mid+

1,r,l,r);}

//線段樹優化建邊

主函式內:

for

(int i=

1;i<=m;

++i)

for(

int j=

1;j++j)

}

最後就是拓撲排序了。把所有節點(包括線段樹節點和虛點)掃一遍,將入度為1的點入隊,然後列舉隊首節點的邊,更新答案,入度減1,若該節點的入度為0,入隊。

需要注意的是,只有遇到初始節點,即節點編號在1

11~n

nn的節點,答案才會加1,否則不加1.

**:

void

topusort()

}while

(hd}}

因為涉及拓撲排序,所以在加邊時一定要統計節點的入度。

#include

#include

#define maxn 4000005

#define lid ls[k]

#define rid rs[k]

using

namespace std;

struct node

tr[maxn*10]

;int n,m,rt,ndnum,ans;

int id[maxn]

,dp[maxn]

,nw[maxn]

,in[maxn]

;int ls[maxn*4]

,rs[maxn*4]

,fl[maxn*4]

;//線段樹

int tot,head[maxn]

;int hd,top,que[maxn]

;void

add(

int x,

int y)

void

build

(int

&k,int l,

int r)

k=++ndnum;

//傳新編號

int mid=

(l+r)

>>1;

build

(lid,l,mid)

;build

(rid,mid+

1,r)

;//下傳左右兒子

add(lid,k)

;add

(rid,k)

;//向父節點連邊

}void

addtr

(int k,

int l,

int r,

int l,

int r)

int mid=

(l+r)

>>1;

if(l<=mid)

addtr

(lid,l,mid,l,r);if

(r>mid)

addtr

(rid,mid+

1,r,l,r);}

//線段樹優化建邊

void

topusort()

}while

(hdintmain()

for(

int j=

1;j++j)

}topusort()

;printf

("%d"

,ans)

;return0;

}

其實線段樹優化建邊好像也沒有那麼難,但是我早在兩個月以前就想學了,直至今天我才看懂。也許針對這道普及組的資料,用線段樹優化建邊顯得小題大做了,但是面對更大的資料,只有這樣才能保證複雜度。最後,感謝機房大佬zjh同學,借助他的模板,我才看懂了線段樹優化建邊(其實我套用的就是他的模板),在此推薦他的部落格pluto_xz的部落格。

線段樹優化建邊

對於許多的圖論題,有些題可能會出現結點要與區間內的每個點都進行建邊,但是如果進行遍歷建邊,那麼複雜度可能會被卡到 o n 2 此時線段樹的騷操作就出來了。我們知道線段樹的每個結點就是乙個區間,那麼我們就可以把點與區間建邊轉換為點與線段樹上的結點進行連邊。以下是幾道線段樹優化建邊的題目幫助您理解這個思...

線段樹優化連邊

有的時候,對於乙個點,向一段區間內所有點連邊 邊數是 o n 2 的 複雜度 於是就有了線段樹優化連邊.線段樹優化連邊,利用到線段樹的思想.對於每個區間,新建乙個節點,向子區間遞迴連邊.這樣,當連向一段區間,就等於連向了所有其子節點 十分抽象.看幾道例題 題目鏈結 暴力做法很顯然 連完邊後跑 tar...

車站分級 (線段樹優化建邊 拓撲序最長路)

10.11 思路 基本方法就是等級高的車站向等級低的車站連邊,最後跑拓撲序的最長路就是ans。線段樹優化建邊的拓撲排序 線段樹的神奇應用 先是建虛點優化,邊數優化為2 n,但是發現建邊的複雜度是nm,考慮線段樹優化。注意到經停站把車站序列劃分成了多個區間,每個區間對應o log 個線段樹上的節點,因...