洛谷P3387 (模板)縮點

2022-05-26 20:42:12 字數 2718 閱讀 9097

題目鏈結

縮點+dp

給定乙個\(n\)個點\(m\)條邊有向圖,每個點有乙個權值,求一條路徑,使路徑經過的點權值之和最大。你只需要求出這個權值和。

允許多次經過一條邊或者乙個點,但是,重複經過的點,權值只計算一次。

第一行,\(n\),\(m\)

第二行,\(n\)個整數,依次代表點權

第三至\(m+2\)行,每行兩個整數\(u\),\(v\),表示\(u\)->\(v\)有一條有向邊

共一行,最大的點權之和。

輸入 #1

2 2

1 11 2

2 1

輸出 #1
2
\(n<=10^4\),\(m<=10^5\),\(0<=點權<=1000\)

演算法:tarjan縮點+dagdp

縮點的模板題。

題目中因為沒有限制走過的邊不能走,所以如果在乙個強連通分量裡,這個強連通分量裡的每個點都可以加到\(ans\)裡,所以我們只要把每個強連通分量縮成乙個點,再搜尋就行了。

如果不縮點直接搜尋會tle哦。

縮點其實就是在原圖上找強聯通分量,然後把每乙個強連通分量作為乙個點建乙個新圖。

那麼重點就在搜尋強聯通分量上了,這裡我們可以用tarjan來搜尋,時間複雜度是線性的。(如果用dfs暴搜強聯通分量會tle,資料範圍很能卡)

博主在這裡略講一下tarjan吧。(博主只是略講,根據博主自己的理解,如果無法理解的可以先自行學習tarjan)

tarjan:

這裡設\(dfn[j]\)為搜尋到點\(j\)時的時間,\(low[j]\)表示在當前強聯通分量裡最小的\(dfn\)值。

最開始時每個點的\(low\)值和\(dfn\)值相等。

這裡我們用乙個棧維護在當前強連通分量裡的點。

tarjan就是用dfs把圖搜尋一遍,遇到沒有搜過的點就去搜它,然後用它的\(low\)值更新當前點的\(low\)值。

如果遇到在棧裡的點時就用這個點的\(dfn\)值更新當前點的\(low\)值。

這裡用\(dfn\)值更新而不用\(low\)值更新其實在求強聯通分量的時候是沒有影響的,因為\(low\)值更新後只會變小,不管變成多少都不再會是當前點的\(dfn\)值,而求強聯通分量時只要區分這個點是不是所在強聯通分量的根(dfs搜尋樹上子樹的根)就行了。

但是在求割點時用\(dfn\)值更新\(low\)值就會出bug。(有關割點的知識在這裡不做詳解)

在搜完回溯時如果當前節點的\(low\)值和\(dfn\)值是一樣的,那麼這個點就是子樹的根了,我們在這裡開始彈出再棧中當前強聯通分量的點。

因為我們的目的是縮點,所以在彈出的時候要把所有強聯通分量中的點指向它們的根。

建新圖:

tarjan結束後我們開始建乙個新圖。

把每個原圖的邊列舉一遍,新建一條從舊邊起始節點所在的強連通分量的根到這條邊所到達的節點所在的強連通分量的根的邊。(這句話讀著比較拗口,大家可以感性理解一下)

注意當新邊的兩個節點相同時(也就是兩個原節點在同乙個強聯通分量裡)就不要建這條邊了。

這裡可以用在dfs時列舉的方法列舉,而不是從1到m列舉,這樣可以確定當前邊的起始節點。

當然你也可以在建原圖的邊時增加乙個\(from\)變數。

因為我們把所有的強聯通分量縮成了乙個點,所以剩下的圖將不會有後向邊(也就是環)。

顯而易見,新圖是乙個拓撲圖。

因為題目只是求最大權值,沒有起點和終點,所以我們可以用每次刪除入度為0的點來更新最大的權值。(只是覺得這麼做跑得比較快)

注意:不要把新圖的點/邊和舊圖的點/邊弄混了,博主因為手殘打錯了導致debug耗費了好長時間。

上**:

#includeusing namespace std;

int n,m,a[20009],u,v;

int dfn[20009],low[20009];

struct aap[200009],p2[200009];

int h[20009],len;

int h2[20009],len2;

void add(int x,int y)

bool k[10009];

int uu[10009],lu;

int t;

int is[10009];

void tj(int x)else if(k[p[j].to]) low[x]=min(low[x],dfn[p[j].to]);

} if(dfn[x]==low[x])

is[uu[lu]]=x;

k[uu[lu--]]=0; }}

int dis[10009];

int in[10009];

int up[100009],l=1,r=0;

int ans=0;

int main()

for(int j=1;j<=n;j++)

if(!dfn[j]) tj(j);//原圖可能是不連通的

for(int j=1;j<=n;j++)

} }for(int j=1;j<=n;j++)//查詢入度為0的點

if(in[is[j]]==0 && is[j]==j)

while(l<=r)

l++;

} for(int j=1;j<=n;j++)

ans=max(ans,dis[j]);

printf("%d",ans);

return 0;

}

模板 縮點 洛谷p3387

縮點 dp 給定乙個n個點m條邊有向圖,每個點有乙個權值,求一條路徑,使路徑經過的點權值之和最大。你只需要求出這個權值和。允許多次經過一條邊或者乙個點,但是,重複經過的點,權值只計算一次。輸入格式 第一行,n,m 第二行,n個整數,依次代表點權 第三至m 2行,每行兩個整數u,v,表示u v有一條有...

洛谷P3387 模板 縮點

kma縮點之後很明顯是個dag,跑一遍toposort順便dp 方程 ans v max ans u w v ans v ans是到這個連通塊的最大點權和,w是這個連通塊的點權 注意縮點之後兩個連通塊之間可能會有很多邊,從連通塊內不同的點發出,正反分別只能連一次,否則toposort會涼 inclu...

縮點 洛谷P3387

給定乙個n個點m條邊有向圖,每個點有乙個權值,求一條路徑,使路徑經過的點權值之和最大。你只需要求出這個權值和。允許多次經過一條邊或者乙個點,但是,重複經過的點,權值只計算一次。第一行,n,m。第二行,n個整數,依次代表點權。第三至m 2行,每行兩個整數u,v,表示u v有一條有向邊。共一行,最大的點...