給定乙個大小為 n
nn 的樹,它共有 n
nn 個結點與 n−1
n - 1
n−1 條邊,結點從 1∼n
1 \sim n
1∼n 編號。初始時每個結點上都有乙個 1∼n
1 \sim n
1∼n 的數字,且每個 1∼n
1 \sim n
1∼n 的數字都只在恰好乙個結點上出現。
接下來你需要進行恰好 n−1
n - 1
n−1 次刪邊操作,每次操作你需要選一條未被刪去的邊,此時這條邊所連線的兩個結點上的數字將會交換,然後這條邊將被刪去。
n −1
n - 1
n−1 次操作過後,所有的邊都將被刪去。此時,按數字從小到大的順序,將數字 1∼n
1 \sim n
1∼n 所在的結點編號依次排列,就得到乙個結點編號的排列 p
ip_i
pi。現在請你求出,在最優操作方案下能得到的字典序最小的 p
ip_i
pi。
我們不難有乙個貪心的想法:列舉每個數,將它們分別移動到盡可能小的節點上面去。
考慮把乙個數字送到另乙個數字上,對於乙個點我們一共有三種限制:
那麼這樣一來我們將刪的邊的順序排成一排,可以發現我們可以用雙向鍊錶來維護。於是我們給每個節點 u
uu 都建立乙個雙向鍊錶。
但在計算答案的過程中,一定有一些是連不到一起的,這種情況我們直接把它們看成一堆鍊錶就可以了。
於是就可以用兩次 dfs 解決這個問題:
列舉每個數字,第一次 dfs 找出它能夠到達的最小的節點,第二次 dfs 更新鍊錶。
總時間複雜度 o(n
2)
o(n^2)
o(n2) 。
各種情況的討論還是看**吧。。。太複雜了 我不想寫 寫不出來。。。
簡直是分類大討論啊。。。
#include
#include
using
namespace std;
const
int maxn =
2000
;struct edge
;edge pool[maxn *2+
5];edge *g[maxn +5]
,*ecnt;
inline
void
addedge
(int u,
int v)
int n, num[maxn +5]
;int pre[maxn +5]
[maxn +5]
, nxt[maxn +5]
[maxn +5]
;//鍊錶的前後指標
int head[maxn +5]
[maxn +5]
, tail[maxn +5]
[maxn +5]
;//鍊錶的頭節點和尾節點
int len[maxn +5]
[maxn +5]
;//鍊錶的大小
int deg[maxn +5]
;inline
void
clear()
int minp;
void
dfs1
(int u,
int fa)
}else
}else
dfs1
(nxt[u]
[fa]
, u)
;//否則只能夠按照我們之前已經確定了的路徑找}}
inline
void
merge
(int u,
int st,
int ed)
//將兩個鍊錶連在一起
bool
dfs2
(int u,
int fa)
int st = head[u]
[fa]
, ed = tail[u]
[fa];if
(fa == n +1)
}}else}}
else
dfs2
(nxt[u]
[fa]
, u)
;//按照既定路徑找下去
}return
false;}
intmain()
if(n ==1)
for(
int i =
1; i <= n; i++)}
return0;
}
CSP S2019 D2T3 樹的重心
小簡單正在學習離散數學,今天的內容是圖論基礎,在課上他做了如下兩條筆記 乙個大小為 n nn 的樹由 n nn 個結點與 n 1 n 1 n 1 條無向邊構成,且滿足任意兩個結點間有且僅有一條簡單路徑。在樹中刪去乙個結點及與它關聯的邊,樹將 為若干個子樹 而在樹中刪去一條邊 保留關聯結點,下同 樹將...
CSP S 2019 樹上的數(樹上推理)
過了這麼久看看自己要多久才能切這題,發現還是想歪了一次。先考慮暴力的做法。還是貪心的逐位確定,逐位確定判有沒有解,相當於下面的問題 樹上有一些路徑,一條路徑表示要把 x 的數字換到 y 去,問有沒有解。對於一條路徑 p 1 p 2 p m 限制如下 1.p 1 p 2 是 p 1 的所有相鄰邊中時間...
SCOI 2019 D1T1 跳躍遊戲
傳送門 有 mm m 個小球,n nn 個島。其中第乙個 最後乙個島以及中間的第 k kk 個島 1 k 1 k n 1 k是固定的,其餘島是懸浮的。一開始所有小球都在第乙個島,你的目標是把他們都移動到最後乙個島,並且使用的步數盡量小。每回合你可以移動乙個小球到它左邊或者右邊的那個平台,但是有一些限...